2

我已经创建了 IDispatchMessageInspector 接口的自定义实现,并且我的代码运行良好 99%。

我的问题是,当 WCF 服务主机被杀死和/或释放我的类的实例时,我需要释放一些托管对象。我要释放的对象实现 IDisposable 但它们没有被处置。我已经浏览了 MSDN 库(更加困惑)和 SO 档案,但没有找到任何可以解决“WCF 服务主机何时/何处销毁 MessageInspectors?”这个问题的东西。

我需要在某个地方挂钩事件吗?我是否需要从 ServiceModel 命名空间实现更神秘的东西?

谁能给我一个正确方向的指针?

编辑 1:澄清

目前,我正在使用自动网络服务器在 IDE 中运行。一旦投入生产,我最终无法控制主机,可能是任何有效的服务器主机选择。

MyCore.My 和 MyCore.MyProperties 对象是我试图在 WCF 服务器主机被杀死/退回时处理的对象。

即使我杀死了网络服务器进程(任务栏中的那些东西),也永远不会调用 Dispose()。

编辑 2:添加了代码片段。

using /* snip */
using MyCore = Acme.My;

namespace My.SOAP
{
    public class MyMessageInspector : IDispatchMessageInspector
    {
        protected static MyCore.My _My;
        protected static MyCore.MyProperties _MyProps;
        protected static ConcurrentDictionary<string, MyCore.AnotherSecretThing> _anotherSecretThings = new ConcurrentDictionary<string, MyCore.AnotherSecretThing>();

        protected static void InitMy()
        {
            if (_My != null) return;

            _MyProps = new MyCore.MyProperties();
            MyCore.MySqlDatabaseLogger logger = new MyCore.MySqlDatabaseLogger(_MyProps);
            _My = new MyCore.My(logger);
        }

        public MyMessageInspector()
        {
            InitMy();
        }

        public object AfterReceiveRequest(ref System.ServiceModel.Channels.Message request,    System.ServiceModel.IClientChannel channel, System.ServiceModel.InstanceContext instanceContext)
        {
            MyMessageHeader header = null;
            try
            {
                // find My header
                Int32 headerPosition = request.Headers.FindHeader(MyMessageHeaderKey.MyHeaderElementName, MyMessageHeaderKey.MyNamespace);
                // get reader
                XmlDictionaryReader reader = request.Headers.GetReaderAtHeader(headerPosition);
                // get My header object
                header = MyMessageHeader.ReadHeader(reader);
                // add to incoming messages properties dictionary
                OperationContext.Current.IncomingMessageProperties.Add(MyMessageHeaderKey.MyHeaderElementName, header);
            }
            catch (Exception ex)
            {
                // log via ExceptionHandlingBlock
            }

            MyCore.SecretThings SecretThings = CreateSecretThings(/* snip */);
            return SecretThings.Id;
        }

        public void BeforeSendReply(ref System.ServiceModel.Channels.Message reply, object  correlationState)
        {
             MyCore.SecretThings req = _My.GetSecretThingsOnThread();
            // if we didn't find the SecretThings, there is nothing to stop() and no data to put in the MessageHeaders
            if (req == null) return;

            MessageBuffer buffer = reply.CreateBufferedCopy(Int32.MaxValue);
            reply = buffer.CreateMessage();

            var MyHeader = new MyMessageHeader(/* snip */);
            reply.Headers.Add(MyHeader);
            req.Stop(MyCore.Status.SUCCESS);
        }

        protected MyCore.SecretThings CreateSecretThings(string key, Dictionary<string, string> ids)
        {
            /* snip */
            return _My.GetSecretThings(/* snip */);
        }
    }
}
4

1 回答 1

2

我一直在查看 DispatchMessageInspector 以及它是如何实现的。

您可能知道您使用 IEndpointBehavior 注册 MessageInspectors(通过配置或代码添加端点行为)。您在 EndpointBehaviour 中创建 DispatchMessageInspector 的实例。

   public class MyBehaviour : IEndpointBehavior
{     

    public void AddBindingParameters(ServiceEndpoint endpoint,
       System.ServiceModel.Channels.BindingParameterCollection
                                            bindingParameters)
    {
    }

    public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
    {

    }

    public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
    {
        var inspector = new SampleMessageInspector(); //register
        endpointDispatcher.DispatchRuntime.MessageInspectors.Add(inspector);
    }

    public void Validate(ServiceEndpoint endpoint)
    {
    }

}

根据http://msdn.microsoft.com/en-us/magazine/cc163302.aspx端点行为由服务主机注册

这些行为集合会在 ServiceHost 和 ChannelFactory 构建过程中自动填充代码中(通过属性)或配置文件中的任何行为(稍后会详细介绍)。您还可以在构建后手动将行为添加到这些集合中。以下示例显示如何将 ConsoleMessageTracing 作为服务行为添加到主机:

ServiceHost host = new ServiceHost(typeof(ZipCodeService)); host.Description.Behaviors.Add(new ConsoleMessageTracing());

并进一步规定 ServiceHost 的生命周期与服务一样长...

ServiceHost 扩展对象在 ServiceHost 的生命周期内保留在内存中,而 InstanceContext 和 OperationContext 扩展对象仅在服务实例或操作调用的生命周期内保留在内存中。您的自定义调度程序/代理扩展可以使用这些集合在整个管道中存储(和查找)用户定义的状态。

我假设这就是为什么您的 MessageInspectors 中的对象永远不会被破坏的原因。

有些人会认为它是一种反模式,但我可能会推荐一个 ServiceLocator,您的 MessageInspector 可以使用它来检索对象。然后你可以看看设置他们的生命周期,只要它的父母使用?

    public class SampleMessageInspector : IDispatchMessageInspector
{             

    public object AfterReceiveRequest(ref System.ServiceModel.Channels.Message request, IClientChannel channel, InstanceContext instanceContext)
    {
        var objectToDispose = ServiceLocator.Current.GetInstance<ObjectToDispose>();

        //do your work
        return null;
    }

    public void BeforeSendReply(ref System.ServiceModel.Channels.Message reply, object correlationState)
    {
        //do some other work
    }
}

继续我提到的...

作为示例,这篇文章提到使用 Ninject 作为 IoC 容器,并将对象的生命周期设置为 WCF 服务的生命周期

绑定(...).To(...).InScope(() => OperationContext.Current)

存储库上的 Ninject WCF 垃圾收集

然后,您可以通过 ServiceLocator 访问 Ninject 内核,并且对象(_MyProperties 等...)将被处理掉

于 2012-07-17T21:09:16.477 回答