2

基本的に単純なシナリオを機能させようとしている実験的なコードがいくつかあります。複数のサービスにデータをストリーミングしている 1 つのクライアントがあります。私が抱えている問題は、サービスの 1 つが正常にシャットダウンしない場合、処理できないように見える EndpointNotFoundException が発生することです。以下は、失敗しているこれを処理する私の試みです。実際には、失敗したサービス チャネルをチャネルのリストから削除し、まだ稼働しているサービスへのデータのストリーミングを続行したいと考えています。タイマー機能は、データ ストリーミングが開始される前にサービスを開始する機会を与えるだけです。

let prices = returns a seq of data that is streamed.

type ReplayDataStream(prices) =
  let evt = new Event<_>()
  member x.Replay() = 
                    async { for line, delay in prices do
                                do! Async.Sleep(delay)
                                evt.Trigger(line) }
                                |> Async.StartImmediate

  member x.PriceChanged = evt.Publish


let main() =
    let addresses = new ResizeArray<EndpointAddress>()

    let announcementService = new AnnouncementService()

    let createChannels addresses =
        let channels = new ResizeArray<IInputDataService>()
        for (address:EndpointAddress) in addresses do
                let channelFactory = new ChannelFactory<IInputDataService>(new BasicHttpBinding(), address)
                let channel = channelFactory.CreateChannel()
                (channel :?> ICommunicationObject).Faulted.Add(fun x -> 
                                                                        (channel :?> ICommunicationObject).Abort()
                                                                        channels.Remove(channel) |> ignore
                                                               )
                channels.Add(channel)
        channels

    let sendMessage(args:ElapsedEventArgs) =
        let channels = createChannels addresses
        for financialDataStream in prices do
        let replayDataStreamA = new ReplayDataStream(financialDataStream)
        for channel in channels do
            try
            //This is where it blows up and the try block isn't catching the exception.
            replayDataStreamA.PriceChanged.Add(channel.InputStringData)
            with
            | :? EndpointNotFoundException as ex -> Console.WriteLine(ex.ToString())
            | :? CommunicationException as ex -> Console.WriteLine(ex.ToString())
            | :? Exception as ex -> Console.WriteLine(ex.ToString())
            replayDataStreamA.Replay()

    let timer = new System.Timers.Timer()
    timer.Enabled <- true
    timer.AutoReset <- false
    timer.Interval <- 30000.0
    timer.Start()
    timer.Elapsed.Add(sendMessage)

    announcementService.OnlineAnnouncementReceived.Add(fun e -> 
                                                                Console.WriteLine(e.EndpointDiscoveryMetadata.Address)
                                                                addresses.Add(e.EndpointDiscoveryMetadata.Address)
                                                                )

    announcementService.OfflineAnnouncementReceived.Add(fun e -> 
                                                                Console.WriteLine(e.EndpointDiscoveryMetadata.Address)
                                                                addresses.Remove(e.EndpointDiscoveryMetadata.Address) |> ignore
                                                                )

    let announcementServiceHost = new ServiceHost(announcementService)
    try
        announcementServiceHost.AddServiceEndpoint(new UdpAnnouncementEndpoint());
        announcementServiceHost.Open();
    with 
    | :? System.ServiceModel.CommunicationException as ex -> Console.WriteLine(ex.ToString())
    | :? System.TimeoutException as ex -> Console.WriteLine(ex.ToString())


    printfn "%s" "Hit any key to close."
    Console.ReadKey() |> ignore
4

2 に答える 2

2

C# でコードを書き直した後、最終的に自分が間違っていたことに気づきました。PriceChanged イベント ハンドラーは次のようになります。ラムダ自体の中で例外をキャッチする必要がありました。ここで、実際に製品コードのように見えるものを作成する必要があります。:)

replayDataStreamA.PriceChanged.Add( fun x -> 
                                                            try
                                                            channel.InputStringData x
                                                            with 
                                                            | :? System.ServiceModel.CommunicationException as ex -> (channel :?> ICommunicationObject).Abort()
                                                            )

後世のために、メソッド全体を次に示します。

let sendMessage(args:ElapsedEventArgs) =
            if(addresses.Count > 0) then
                for address in addresses do
                    let channelFactory = new ChannelFactory<IInputDataService>(new BasicHttpBinding(), address)
                    let channel = channelFactory.CreateChannel()
                    for financialDataStream in prices do
                    let replayDataStreamA = new ReplayDataStream(financialDataStream)
                    replayDataStreamA.PriceChanged.Add( fun x -> 
                                                        try
                                                        channel.InputStringData x
                                                        with 
                                                        | :? System.ServiceModel.CommunicationException as ex -> (channel :?> ICommunicationObject).Abort()
                                                        )
                    replayDataStreamA.Replay()
于 2011-05-27T19:56:05.130 に答える
0

Sky Sanders による説明は非常に理にかなっていて、このシナリオでうまくいくはずです。ブログへのリンクはこちらです。

Faulted イベントにサブスクライバーを提供しても、例外ハンドラー内で channel.Abort() を呼び出すのとまったく同じことはできません。

PriceChanged.Add() は PriceChanged += と同等です: ハンドラーを Price changed イベントにサブスクライブしています。try/with ブロックを配置すると、サブスクライブ中にスローされた例外がキャッチされます (イベントでのカスタムの追加/削除の実装を考えてください)。それはあなたが望むものではありません。InputStringData を呼び出すときに例外を処理する方法を探しています。この思考プロセスは、自然に解決策につながります

C# の運用コードでは、イベント側で例外が発生するポイントの周りに try/catch ブロックを配置します。サブスクライバーと Debug.Assert によってスローされた例外をキャッチし、すべての例外をサブスクライバー側で処理する必要があることを開発者に警告します。コードでは、これは evt.Trigger() で警告して再スローする try/with ブロックを意味します。

宣言の時点で実行する代わりに、非同期ブロックを公開できます。これにより、sendMessage 内で、より高いレベルでのオーケストレーション機能が提供されます。例外をキャッチし、キャンセルとタイムアウトを 1 か所で処理するための特別な API があります

于 2011-05-27T03:55:40.360 に答える