使用 WCF 服务模型从 SQL 接收查询通知

本主题演示如何将 SQL 适配器配置为从 SQL Server 数据库接收查询通知消息。 若要演示通知,请考虑一个表 Employee,其中包含“状态”列。 向此表插入新记录时,“状态”列的值将设置为 0。 可以通过使用 SQL 语句注册通知来将适配器配置为接收通知,该语句检索状态列为“0”的所有记录。 为此,可以为 NotificationStatement 绑定属性指定 SQL 语句。 适配器客户端收到通知后,可以包含对SQL Server数据库执行任何后续任务的逻辑。 在此示例中,为简单起见,适配器客户端列出了表中“状态”列为“0”的所有记录。

注意

如果要对具有用户定义类型的列的表执行操作,请确保在开始开发应用程序之前参考 使用 SQL 适配器对具有用户定义类型的表和视图 的操作主题。

使用 SQL 适配器绑定属性配置通知

下表汇总了用于配置从 SQL Server 接收通知的 SQL 适配器绑定属性。 必须在运行 .NET 应用程序时指定这些绑定属性,才能从SQL Server数据库接收通知。

Binding 属性 说明
InboundOperationType 指定要执行的入站操作。 若要接收通知消息,请将此项设置为 “通知”。
NotificationStatement 指定用于注册查询通知的 SQL 语句 (SELECT 或 EXEC <存储过程>) 。 仅当指定 SQL 语句的结果集发生更改时,适配器才会从SQL Server获取通知消息。
NotifyOnListenerStart 指定在启动侦听器时适配器是否向适配器客户端发送通知。

有关这些属性的更完整说明,请参阅阅读有关 SQL Server 适配器绑定属性的 BizTalk 适配器。 有关如何使用 SQL 适配器接收来自SQL Server通知的完整说明,请进一步阅读。

使用 WCF 服务模型配置通知

若要使用 WCF 服务模型接收通知,必须:

  1. 从适配器公开的元数据生成 WCF 服务协定 (接口) 通知 操作。 为此,可以使用添加适配器服务引用插件。

  2. 为 Employee 表上的 Select 操作生成 WCF 客户端。 为此,可以使用添加适配器服务引用插件。

  3. 从此接口实现 WCF 服务。

  4. 使用服务主机 (System.ServiceModel.ServiceHost) 托管此 WCF 服务。

关于本主题中使用的示例

本主题中的示例接收 Employee 表的通知。 示例提供了用于生成表的脚本。 有关示例的详细信息,请参阅 SQL 适配器的示例。 SQL 适配器示例还提供了基于本主题 的示例 Notification_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="NotificationOperation")]
public interface NotificationOperation {

    // CODEGEN: Generating message contract since the wrapper namespace (https://schemas.microsoft.com/Sql/2008/05/Notification/) of message
    // Notification does not match the default value (https://schemas.microsoft.com/Sql/2008/05/)
    [System.ServiceModel.OperationContractAttribute(IsOneWay=true, Action="Notification")]
    void Notification(Notification request);
}

消息协定

下面是通知操作的消息协定。

[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")]
[System.ServiceModel.MessageContractAttribute(WrapperName="Notification", WrapperNamespace="http://schemas.microsoft.com/Sql/2008/05/Notification/", IsWrapped=true)]
public partial class Notification {

    [System.ServiceModel.MessageBodyMemberAttribute(Namespace="http://schemas.microsoft.com/Sql/2008/05/Notification/", Order=0)]
    public string Info;

    [System.ServiceModel.MessageBodyMemberAttribute(Namespace="http://schemas.microsoft.com/Sql/2008/05/Notification/", Order=1)]
    public string Source;

    [System.ServiceModel.MessageBodyMemberAttribute(Namespace="http://schemas.microsoft.com/Sql/2008/05/Notification/", Order=2)]
    public string Type;

    public Notification() {
    }

    public Notification(string Info, string Source, string Type) {
        this.Info = Info;
        this.Source = Source;
        this.Type = Type;
    }
}

WCF 服务类

添加适配器服务引用插件还会生成一个文件,该文件包含从服务协定 (接口) 实现的 WCF 服务类的存根。 文件的名称为 SqlAdapterBindingService.cs。 可以插入逻辑以直接在此类中处理 通知 操作。 以下代码显示由添加适配器服务引用插件生成的 WCF 服务类。

namespace SqlAdapterBindingNamespace {

    public class SqlAdapterBindingService : NotificationOperation {

        // CODEGEN: Generating message contract since the wrapper namespace (https://schemas.microsoft.com/Sql/2008/05/Notification/)
        // of message Notification does not match the default value (https://schemas.microsoft.com/Sql/2008/05/)
        public virtual void Notification(Notification request) {
            throw new System.NotImplementedException("The method or operation is not implemented.");
        }
    }
}

使用 WCF 服务模型接收查询通知

本部分提供有关如何编写 .NET 应用程序以使用 SQL 适配器接收查询通知的说明。

接收查询通知

  1. 使用“添加适配器服务引用”插件为 Employee 表上的 Select 操作生成 WCF 客户端。 在收到通知消息后,将使用此客户端执行 Select 操作。 向项目添加新类 TableOperation.cs,并添加以下代码片段以执行 Select 操作。

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    
    namespace Notification_ServiceModel
    {
        public class TableOperation
        {
            public void TableOp()
            {
                ///////////////////////////////////////////////////////////////////////
                // CREATING THE CLIENT
                ///////////////////////////////////////////////////////////////////////
    
                TableOp_dbo_EmployeeClient client = new TableOp_dbo_EmployeeClient("SqlAdapterBinding_TableOp_dbo_Employee");
    
                client.ClientCredentials.UserName.UserName = "<Enter user name here>";
                client.ClientCredentials.UserName.Password = "<Enter password here>";
    
                ///////////////////////////////////////////////////////////////////////
                // OPENING THE CLIENT
                ///////////////////////////////////////////////////////////////////////
    
                try
                {
                    Console.WriteLine("Opening Client...");
                    client.Open();
                }
                catch (Exception ex)
                {
                    Console.WriteLine("Exception: " + ex.Message);
                    throw;
                }
    
                ///////////////////////////////////////////////////////////////////////
                // SELECTING THE LAST INSERTED RECORD FROM THE TABLE
                ///////////////////////////////////////////////////////////////////////
                schemas.microsoft.com.Sql._2008._05.Types.Tables.dbo.Employee[] selectRecords;
    
                try
                {
                    selectRecords = client.Select("*", "where Status=0");
                }
    
                catch (Exception ex)
                {
                    Console.WriteLine("Exception: " + ex.Message);
                    throw;
                }
    
                Console.WriteLine("The details of the newly added employee are:");
                Console.WriteLine("********************************************");
                for (int i = 0; i < selectRecords.Length; i++)
                {
                    Console.WriteLine("Employee Name      : " + selectRecords[i].Name);
                    Console.WriteLine("Employee Designation: " + selectRecords[i].Designation);
                    Console.WriteLine("Employee Status    : " + selectRecords[i].Status);
                    Console.WriteLine();
                }
                Console.WriteLine("********************************************");
    
    

    重要

    由于此代码片段对包含 Point UDT 列的 Employee 表执行操作,因此请确保在运行应用程序时将 UDT DLL 放在项目的 \bin\Debug 文件夹下。

  2. 使用“添加适配器服务引用”插件生成 WCF 服务协定, (通知 操作的接口) 和帮助程序类。

    有关详细信息,请参阅为SQL Server项目生成 WCF 客户端或 WCF 服务协定。 可以选择在生成服务协定和帮助程序类时指定绑定属性。 这可以保证在生成的配置文件中正确设置它们。

  3. 从步骤 2 中生成的接口和帮助程序类实现 WCF 服务。 如果处理从通知操作收到的数据时遇到错误,此类的 Notification 方法可能会引发异常来 中止操作; 否则, 方法不返回任何内容。 必须按如下所示对 WCF 服务类进行特性化:

    [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
    

    Notification 方法中,可以直接实现应用程序逻辑。 可以在 SqlAdapterBindingService.cs 中找到此类。 此示例中的此代码子类为 SqlAdapterBindingService 类。 在此代码中,收到的通知消息将写入控制台。 此外,调用 TableOperation 类中的 TableOp 方法以执行 Select 操作。

    [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
    
    public class NotificationService : SqlAdapterBindingNamespace.SqlAdapterBindingService
    {
        public override void Notification(Notification request)
        {
            Console.WriteLine("\nNew Notification Received");
            Console.WriteLine("*************************************************");
            Console.WriteLine(request.Info);
            Console.WriteLine(request.Source);
            Console.WriteLine(request.Type);
            Console.WriteLine("*************************************************");
    
            // Invoke th TableOp method in the TableOperation class
            TableOperation Ops = new TableOperation();
            Ops.TableOp();
        }
    }
    
  4. 由于 SQL 适配器不接受凭据作为连接 URI 的一部分,因此必须实现以下类才能传递SQL Server数据库的凭据。 在应用程序的后一部分中,将实例化此类以传递SQL Server凭据。

    class NotificationCredentials : 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 NotificationCredentials();
            clone.UserName.UserName = this.UserName.UserName;
            clone.UserName.Password = this.UserName.Password;
            return clone;
        }
    }
    
  5. 创建 SqlAdapterBinding ,并通过指定绑定属性将适配器配置为接收查询通知。 可以在代码中显式执行此操作,也可以在配置中以声明方式执行此操作。 至少必须指定 InboundOperationTypeNotificationStatement 绑定属性。

    SqlAdapterBinding binding = new SqlAdapterBinding();
    binding.InboundOperationType = InboundOperation.Notification;
    binding.NotificationStatement = "SELECT Employee_ID, Name FROM dbo.Employee WHERE Status=0";
    binding.NotifyOnListenerStart = true;
    
  6. 通过实例化在步骤 4 中创建的 NotificationCredentials 类,指定SQL Server数据库凭据。

    NotificationCredentials credentials = new NotificationCredentials();
    credentials.UserName.UserName = "<Enter user name here>";
    credentials.UserName.Password = "<Enter password here>";
    
  7. 创建在步骤 3 中创建的 WCF 服务的实例。

    // create service instance
    NotificationService service = new NotificationService();
    
  8. 使用 WCF 服务和基本连接 URI 创建 System.ServiceModel.ServiceHost 的实例。 还必须在此处指定凭据。

    // Enable service host
    Uri[] baseUri = new Uri[] { new Uri("mssql://mysqlserver//mydatabase") };
    ServiceHost serviceHost = new ServiceHost(service, baseUri);
    serviceHost.Description.Behaviors.Add(credentials);
    
    
  9. 将服务终结点添加到服务主机。 为此,请按以下步骤操作:

    • 使用在步骤 5 中创建的绑定。

    • 指定包含凭据的连接 URI,并根据需要指定入站 ID。

    • 将协定指定为“NotificationOperation”。

      // Add service endpoint: be sure to specify NotificationOperation as the contract
      Uri ConnectionUri = new Uri("mssql://mysqlserver//mydatabase?");
      serviceHost.AddServiceEndpoint("NotificationOperation", binding, ConnectionUri);
      
  10. 若要接收通知消息,请打开服务主机。

    // Open the service host to begin receiving notifications
    serviceHost.Open();
    
  11. 若要停止接收通知,请关闭服务主机。

    serviceHost.Close();
    

示例

以下示例演示用于接收 Employee 表的通知消息的 .NET 应用程序。

注意

以下代码片段实例化 TableOperation.cs 类并调用 TableOp 方法。 步骤 1 中介绍了 类和 方法。

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;

namespace Notification_ServiceModel
{
    [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
    public class NotificationService : SqlAdapterBindingNamespace.SqlAdapterBindingService
    {
        public override void Notification(Notification request)
        {
            Console.WriteLine("\nNew Notification Received");
            Console.WriteLine("*************************************************");
            Console.WriteLine(request.Info);
            Console.WriteLine(request.Source);
            Console.WriteLine(request.Type);
            Console.WriteLine("*************************************************");
            TableOperation Ops = new TableOperation();
            Ops.TableOp();
        }
    }

    class NotificationCredentials : 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 NotificationCredentials();
            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
            {
                SqlAdapterBinding binding = new SqlAdapterBinding();
                binding.InboundOperationType = InboundOperation.Notification;
                binding.NotificationStatement = "SELECT Employee_ID, Name FROM dbo.Employee WHERE Status=0";
                binding.NotifyOnListenerStart = true;

                // 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") };

                NotificationCredentials credentials = new NotificationCredentials();
                credentials.UserName.UserName = "<Enter user name here>";
                credentials.UserName.Password = "<Enter password here>";

                Console.WriteLine("Opening service host...");
                NotificationService service = new NotificationService();
                serviceHost = new ServiceHost(service, baseUri);
                serviceHost.Description.Behaviors.Add(credentials);
                serviceHost.AddServiceEndpoint("NotificationOperation", binding, ConnectionUri);
                serviceHost.Open();
                Console.WriteLine("Service host opened...");
                Console.WriteLine("Waiting for notification...");

                Console.WriteLine("\nHit <RETURN> to stop receiving notification");
                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 receiving notifications
                if (serviceHost.State == CommunicationState.Opened)
                    serviceHost.Close();
                else
                    serviceHost.Abort();
            }
        }
    }
}

另请参阅

使用 WCF 服务模型开发 SQL 应用程序