2

IDispatchMessageInspectorインターフェイスのカスタム実装を作成しましたが、コードは99%正常に機能しています。

私の問題は、WCFサービスホストが強制終了されたとき、および/またはクラスのインスタンスを解放したときに、いくつかの管理対象オブジェクトを解放する必要があることです。IDisposableを解放するための私のオブジェクトは、破棄されていません。私はMSDNライブラリ(もっと混乱しています)とSOアーカイブを調べましたが、「WCFサービスホストがMessageInspectorsを破棄するのはいつ/どこですか?」という質問に対処するものは見つかりませんでした。

イベントをどこかにフックする必要がありますか?ServiceModel名前空間からさらに難解なものを実装する必要がありますか?

誰かが私に正しい方向へのポインタを与えることができますか?

編集1:説明

現在、自動Webサーバーを使用してIDEで実行しています。私は、本番環境で一度ホストを制御することはできません。有効なサーバーホストの選択肢のいずれかである可能性があります。

MyCore.MyオブジェクトとMyCore.MyPropertiesオブジェクトは、WCFサーバーホストが強制終了/バウンスされたときに破棄しようとしているオブジェクトです。

Webサーバープロセス(タスクバーにあるもの)を強制終了した場合でも、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とそれがどのように実装されているかを見てきました。

ご存知かもしれませんが、MessageInspectorsをIEndpointBehaviorに登録します(構成またはコードを介してエンドポイントの動作を追加します)。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内のオブジェクトが破棄されない理由であると思います。

一部の人はそれをアンチパターンと見なしますが、MessageInspectorsがオブジェクトを取得するために使用できるServiceLocatorをお勧めします。次に、親が使用している限り、ライフタイムの設定を検討できますか?

    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
    }
}

私が言及したことから続くために...

例として、この投稿では、IoCコンテナーとしてNinjectを使用し、オブジェクトの有効期間をWCFサービスの有効期間として設定することに言及しています。

Bind(...)。To(...)。InScope(()=> OperationContext.Current)

リポジトリにWCFガベージコレクションを挿入します

その後、ServiceLocatorを介してNinject Kernalにアクセスでき、オブジェクト(_MyPropertiesなど)は破棄されます。

于 2012-07-17T21:09:16.477 に答える