完成时间: 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);
}
方法说明包括:
方法 | DESCRIPTION |
---|---|
启动监听器 | 开始侦听消息,使用提供的 WS-Addressing动作。 如果未指定任何动作,它将侦听所有动作或默认动作。 |
停止监听器 | 停止监听。 |
尝试接收 (TryReceive) | 尝试从目标系统接收传入的消息。 |
WaitForMessage | 等待来自目标系统的入站 WCF 消息。 |
有关每个方法参数的说明的更多详细信息,请参阅有关接口的文档 Microsoft.ServiceModel.Channels.Common.IInboundHandler
。
实现 EchoAdpterInboundHandler
回显适配器使用System.IO.FileSystemWatcher
来模拟目标系统。 在下面的 Microsoft.ServiceModel.Channels.Common.IInboundHandler
接口中,你将实现每个方法,包括 StartListener、StopListener、TryReceive 和 WaitForMessage。
在 EchoAdpterInboundHandler 类中实现 IInboundHandler 接口
在解决方案资源管理器中,双击 EchoAdapterInboundHandler.cs 文件。
在 Visual Studio 编辑器中,将以下行添加到现有的 using 指令集。
using System.IO; using System.ServiceModel.Channels; using System.Xml; using System.Diagnostics;
现在,将类级别变量添加到 EchoAdapterInboundHandler 类。 这些变量用于监视文件系统中的文件活动。 将下面的声明复制到构造函数之前的类中。
private Queue<Message> inboundQueue; private FileSystemWatcher inboundWatcher; private Object inboundQueueSynchronizationLock; private string path; private string filter;
在 EchoAdapterInboundHandler 构造函数方法中,添加以下代码来初始化文件监视基础结构,并捕获监视路径和筛选器。
inboundWatcher = null; inboundQueueSynchronizationLock = new Object(); path = connection.ConnectionFactory.Adapter.InboundFileSystemWatcherFolder; filter = connection.ConnectionFactory.Adapter.InboundFileFilter;
现在,将以下代码添加到 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); } }
继续添加 StopListener 方法的实现。
if (inboundWatcher != null) { // End monitoring inboundWatcher.EnableRaisingEvents = false; inboundWatcher = null; } lock (inboundQueueSynchronizationLock) { inboundQueue.Clear(); inboundQueue = null; }
现在提供 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); }
继续添加 WaitForMessage 方法的实现。
while (inboundQueue.Count == 0) { }; Message msg = inboundQueue.Peek(); if (msg != null) { return true; } else { return false; }
现在为文件观察程序设置回调函数。 为此,请将新方法 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); } } } }
现在,必须删除内部 EchoAdapterInboundReply 类引发的 NotImplementedException 异常。 为此,请从 Abort 和 Reply 方法中删除以下语句。
throw new NotImplementedException("The method or operation is not implemented.");
中止和回复方法应如下所示。
/// <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) { }
若要完成入站处理程序的实现,请将以下类添加到 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; } } }
在 Visual Studio 的 “文件 ”菜单上,单击“ 全部保存”。
在 “生成” 菜单上,单击 “生成解决方案” 。 它应编译而不出现错误。 否则,请确保已按照上述每个步骤进行操作。
注释
你保存了你的工作。 此时可以安全地关闭 Visual Studio,也可以转到下一步 步骤:生成和部署 Echo 适配器。
我刚刚做了什么?
在 Echo 适配器教程的此步骤中,你为入站处理程序提供了实现。 此实现使用 .NET Framework 的 FileSystemWatcher 类为 Echo 适配器提供文件监视功能。
后续步骤
在下一步中,部署适配器。