WCF F # - Изящно обрабатывает остановку службы на клиенте - PullRequest
2 голосов
/ 27 мая 2011

У меня есть экспериментальный код, в основном просто пытающийся заставить работать простой сценарий.У меня есть один клиент, который передает данные на несколько служб.У меня проблема в том, что если одна из служб не завершает работу корректно, я получаю исключение 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

Ответы [ 2 ]

2 голосов
/ 27 мая 2011

После того, как я переписал мой код на 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()
0 голосов
/ 27 мая 2011

Объяснение Sky Sanders имеет большой смысл, и должно работать для этого сценария.Вот ссылка на блог.

Предоставление подписчика на событие Faulted не совсем совпадает с вызовом channel.Abort () внутри обработчика исключений.

PriceChanged.Add () является эквивалентом PriceChanged + =: вы подписываете обработчик на событие Измененная цена.Размещение блока try / with будет перехватывать исключения, возникающие при подписке (например, настраиваемая реализация Add / Remove в вашем событии), но это не то, что вам нужно.Вы ищете способ обработки исключения при вызове InputStringData.Этот мыслительный процесс естественным образом приводит к вашему решению .

В рабочем коде C # поместите блок try / catch вокруг точки, где исключение возникает на стороне события.Поймайте исключение, генерируемое подписчиком и Debug.Assert с повторным выбросом, предупреждая разработчика, что все исключения должны быть обработаны на стороне подписчика.В вашем коде это означает блок try / with, который предупреждает и перебрасывает в evt.Trigger ().

Вы можете выставить асинхронный блок вместо того, чтобы запускать его в точке объявления.Это должно предоставить вам возможности оркестровки на более высоком уровне: внутри sendMessage.Существует специальный API для отлова исключений, обработки отмены и тайм-аутов в одном центральном месте, которое действительно стоит изучения .

...