我有一些实验代码,基本上只是想让一个简单的场景工作。我有一个客户端将数据流式传输到多个服务。我遇到的问题是,如果其中一个服务没有正常关闭,我得到一个我似乎无法处理的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
答案 0 :(得分:2)
在C#中重写我的代码之后,我终于意识到我做错了什么。这就是PriceChanged事件处理程序应该是什么样子。我需要在lambda本身内捕获异常。现在我需要写一些看起来像生产代码的东西。 :)
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()
答案 1 :(得分:0)
Sky Sanders的解释很有意义,应该适用于这种情况。这是一个link to the blog.
为Faulted事件提供订阅者与在异常处理程序中调用channel.Abort()完全不同。
PriceChanged.Add()相当于PriceChanged + =:您正在为价格变更事件订阅处理程序。放置一个try / with块将捕获订阅时抛出的异常(想想你事件中的自定义Add / Remove实现),而这不是你想要的。您正在寻找一种在调用InputStringData时处理异常的方法。这个思维过程自然会导致your solution。
在C#生产代码中,在事件端引发异常的位置放置一个try / catch块。使用重新抛出来捕获订阅者抛出的异常和Debug.Assert,警告开发人员应该在订阅者端处理所有异常。在你的代码中,这意味着一个try / with块警告并重新抛出evt.Trigger()。
您可以公开异步块,而不是在声明点处运行它。这应该为您提供更高级别的编排能力:在sendMessage中。有一个特殊的API可以捕获异常,在一个真正值得looking into的中心位置处理取消和超时。