步骤 8:实现 Echo 适配器的同步入站处理程序

第 8 步(共 9

完成时间: 45 分钟

在此步骤中,你将实现回显适配器的入站功能。 此功能允许适配器侦听来自目标系统的数据或事件。 根据 WCF LOB 适配器 SDK,只需在适配器支持入站功能时实现 Microsoft.ServiceModel.Channels.Common.IInboundHandler 接口。 适配器开发向导会自动生成名为 EchoAdpterInboundHandler 的派生类。

在下一部分中,将更新 EchoAdpterInboundHandler 类,以便更好地了解如何实现此接口。 完成此步骤后,将有一个适用于回显适配器的工作入站处理程序。

必备条件

在开始此步骤之前,必须已成功完成 步骤 7:实现 Echo 适配器的同步出站处理程序。 基本熟悉 Microsoft.ServiceModel.Channels.Common.IInboundHandler 也很有帮助。

IInboundHandler 接口

Microsoft.ServiceModel.Channels.Common.IInboundHandler 定义为:

public interface IInboundHandler : IConnectionHandler, IDisposable  
{  
          void StartListener(string[] actions, TimeSpan timeout);  
          void StopListener(TimeSpan timeout);  
          bool TryReceive(TimeSpan timeout, out Message message, out IInboundReply reply);  
          bool WaitForMessage(TimeSpan timeout);  
}  

方法说明如下:

方法 说明
StartListener 使用提供的 WS-Addressing Actions 开始侦听消息。 如果未指定,则侦听所有或默认操作。
StopListener 停止侦听。
TryReceive 尝试从目标系统接收入站消息。
WaitForMessage 等待来自目标系统的入站 WCF 消息。

有关每个方法参数的说明的更多详细信息,请参阅 有关 接口的文档 Microsoft.ServiceModel.Channels.Common.IInboundHandler

实现 EchoAdpterInboundHandler

回显适配器使用 System.IO.FileSystemWatcher 来模拟目标系统。 在下面,你将在接口 StartListener、StopListener、TryReceive 和 WaitForMessage 中 Microsoft.ServiceModel.Channels.Common.IInboundHandler 实现每个方法。

在 EchoAdpterInboundHandler 类中实现 IInboundHandler 接口

  1. 在 解决方案资源管理器中,双击 EchoAdapterInboundHandler.cs 文件。

  2. 在 Visual Studio 编辑器中,将以下行添加到现有的 using 指令集。

    using System.IO;  
    using System.ServiceModel.Channels;  
    using System.Xml;  
    using System.Diagnostics;  
    
  3. 现在,将类级别变量添加到 EchoAdapterInboundHandler 类。 这些变量用于监视文件系统中的文件活动。 将下面的声明复制到 构造函数之前的 类中。

    private Queue<Message> inboundQueue;  
    private FileSystemWatcher inboundWatcher;  
    private Object inboundQueueSynchronizationLock;  
    private string path;  
    private string filter;  
    
  4. 在 EchoAdapterInboundHandler 构造函数方法中,添加以下代码以初始化文件监视基础结构并捕获监视路径和筛选器。

    inboundWatcher = null;  
    inboundQueueSynchronizationLock = new Object();  
    path = connection.ConnectionFactory.Adapter.InboundFileSystemWatcherFolder;  
    filter = connection.ConnectionFactory.Adapter.InboundFileFilter;  
    
  5. 现在,将以下代码添加到 StartListener 方法。 代码实现逻辑以验证参数并开始监视文件活动。

    // if no actions are provided, log an error in the trace log  
    // and throw an exception  
    if (actions.Length == 0)  
    {  
        EchoAdapterUtilities.Trace.Trace(TraceEventType.Error, "http://echoadapterv2/startlistener/noactions", "No operation actions were received for listener to do specific processing.", this);  
        throw new AdapterException("Unable to receive any actions for inbound handler to start listening.");  
    }  
    
    inboundQueue = new Queue<Message>();  
    foreach (string action in actions)  
    {  
        // for the OnReceiveEcho action listen for a new file created event  
        if ("Echo/OnReceiveEcho".Equals(action))  
        {  
            if (inboundWatcher == null)  
            {  
                inboundWatcher = new FileSystemWatcher(path);  
                inboundWatcher.Filter = filter;  
                // Begin monitoring  
                inboundWatcher.EnableRaisingEvents = true;  
            }  
            inboundWatcher.Created += new FileSystemEventHandler(FileMonitor_Created);  
            EchoAdapterUtilities.Trace.Trace(TraceEventType.Information, "http://echoadapterv2/startlistener", "Listening for file created event for " + filter + " in path " + path, this);  
        }  
    }  
    
  6. 通过添加 StopListener 方法的实现继续。

    if (inboundWatcher != null)  
    {  
        // End monitoring  
        inboundWatcher.EnableRaisingEvents = false;  
        inboundWatcher = null;  
    }  
    lock (inboundQueueSynchronizationLock)  
    {  
        inboundQueue.Clear();  
        inboundQueue = null;  
    }  
    
  7. 现在提供 TryReveive 方法的实现。 此方法从内部队列检索最新的文件接收消息(如果有)。

    reply = new EchoAdapterInboundReply();  
    message = null;  
    TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);  
    while (true)  
    {  
        lock (inboundQueueSynchronizationLock)  
        {  
            if (inboundQueue == null)  
            {  
                //listener has been closed  
                return false;  
            }  
            if (inboundQueue.Count != 0)  
            {  
                message = inboundQueue.Dequeue();  
                if (message != null)  
                {  
                    return true;  
                }  
            }  
        }  
        if (timeoutHelper.IsExpired)  
        {  
            return false;  
        }  
        //wait for sometime, and check again  
        System.Threading.Thread.Sleep(500);  
    }  
    
  8. 继续添加 WaitForMessage 方法的实现。

    while (inboundQueue.Count == 0) { };  
    Message msg = inboundQueue.Peek();  
    if (msg != null)  
    {  
        return true;  
    }  
    else  
    {  
        return false;  
    }  
    
  9. 现在为文件观察程序提供回调。 为此,请将 新方法FileMonitor_Created 添加到 EchoAdapterInboundAdapter 类。

    private void FileMonitor_Created(object sender, FileSystemEventArgs e)  
    {  
        lock (inboundQueueSynchronizationLock)  
        {  
            if (e.ChangeType == WatcherChangeTypes.Created)  
            {  
                // wait for file to close - should do this in a better manner  
                System.Threading.Thread.Sleep(500);  
                try  
                {  
                    EchoAdapterUtilities.Trace.Trace(TraceEventType.Information, "http://echoadapterv2/FileMonitorCreated", "File " + e.FullPath + " created.", this);  
                    FileInfo fileInfo = new FileInfo(e.FullPath);  
                    // Create WCF message to send to the inbound service  
                    // that is listening for messages from adapter  
                    String xmlData = String.Format(@"<OnReceiveEcho xmlns=""{0}""><path>{1}</path><length>{2}</length></OnReceiveEcho>", EchoAdapter.SERVICENAMESPACE, e.FullPath, fileInfo.Length);  
                    // set action string  
                    XmlReader reader = XmlReader.Create(new StringReader(xmlData));  
                    // create WCF message  
                    Message requestMessage = Message.CreateMessage(MessageVersion.Default  
                                , "Echo/OnReceiveEcho"  
                                , reader);  
                    requestMessage.Headers.To = new Uri(path);  
                    inboundQueue.Enqueue(requestMessage);  
                }  
                catch (Exception ex)  
                {  
                    String message = String.Format("An exception was thrown while trying to open file {1}.", e.FullPath);  
                    EchoAdapterUtilities.Trace.Trace(System.Diagnostics.TraceEventType.Error, "http://echoadapterv2/FileMonitorCreated", message, this, ex);  
                    throw new AdapterException(message, ex);  
                }  
            }  
        }  
    }  
    
  10. 现在,必须删除内部 EchoAdapterInboundReply 类引发的 NotImplementedException 异常。 为此,请从 AbortReply 方法中删除以下语句。

    throw new NotImplementedException("The method or operation is not implemented.");  
    

    AbortReply 方法应如下所示。

    /// <summary>  
    /// Abort the inbound reply call  
    /// </summary>  
    public override void Abort()  
    {  
    }  
    
    /// <summary>  
    /// Reply message implemented  
    /// </summary>  
    public override void Reply(System.ServiceModel.Channels.Message message  
        , TimeSpan timeout)  
    {  
    }  
    
  11. 若要完成入站处理程序的实现,请将以下类添加到 EchoAdapterOutboundHandler.cs。 此类为入站处理程序实现提供超时支持。

    /// <summary>  
    /// Utility class containing helper functions for measuring timeout   
    /// </summary>  
    class TimeoutHelper  
    {  
        private TimeSpan timeout;  
        private DateTime creationTime;  
        private Boolean isInfinite;  
    
        /// <summary>  
        /// Constructor  
        /// </summary>  
        /// <param name="timeout"></param>  
        public TimeoutHelper(TimeSpan timeout)  
        {  
            this.creationTime = DateTime.Now;  
            this.timeout = timeout;  
            if (timeout.Equals(Infinite)) this.isInfinite = true;  
        }  
    
        /// <summary>  
        /// Value of infinite timespan  
        /// </summary>  
        public static TimeSpan Infinite  
        {  
            get { return TimeSpan.MaxValue; }  
        }  
    
        /// <summary>  
        /// Value indicating remaining timeout  
        /// </summary>  
        public TimeSpan RemainingTimeout  
        {  
            get  
            {  
                if (this.isInfinite) return Infinite;  
                return this.timeout.Subtract(DateTime.Now.Subtract(this.creationTime));  
            }  
        }  
    
        /// <summary>  
        /// Get remaining timeout value and throw an exception if the timeout  
        /// has expired.  
        /// </summary>  
        /// <param name="exceptionMessage"></param>  
        /// <returns></returns>  
        public TimeSpan GetRemainingTimeoutAndThrowIfExpired(String exceptionMessage)  
        {  
            if (this.isInfinite) return Infinite;  
            if (RemainingTimeout < TimeSpan.Zero)  
            {  
                throw new TimeoutException(exceptionMessage);  
            }  
            return RemainingTimeout;  
        }  
    
        /// <summary>  
        /// Throw an exception if the timeout has expired.  
        /// </summary>  
        /// <param name="exceptionMessage"></param>  
        public void ThrowIfTimeoutExpired(String exceptionMessage)  
        {  
            if (RemainingTimeout < TimeSpan.Zero)  
            {  
                throw new TimeoutException(exceptionMessage);  
            }  
    
        }  
    
        /// <summary>  
        /// Value indicating whether timeout has expired.  
        /// </summary>  
        public Boolean IsExpired  
        {  
            get  
            {  
                if (this.isInfinite) return false;  
                return RemainingTimeout < TimeSpan.Zero;  
            }  
        }  
    }  
    
  12. 在 Visual Studio 的“ 文件 ”菜单上,单击“ 全部保存”。

  13. 在“生成”菜单中,单击“生成解决方案”。 它应在编译时不出错。 否则,请确保已执行上述每个步骤。

注意

保存所做的工作。 此时可以安全地关闭 Visual Studio,也可以转到下一步 :步骤 9:生成和部署 Echo 适配器

我只是做什么?

在回显适配器教程的此步骤中,你提供了入站处理程序的实现。 此实现使用 .NET Framework的 FileSystemWatcher 类为 Echo 适配器提供文件监视功能。

后续步骤

在下一步中,部署适配器。

另请参阅

步骤 9:生成并部署 Echo 适配器
步骤 7:实现 Echo 适配器的同步出站处理程序