문제

내가 하려고 쓰는 클래스를 캡슐화하는 WCF 통화(클라이언트가 Silverlight,중요한 경우).그것을 모두 거침없이 작동하지만,나는 확실하지 않는 방법을 트랩 연결이 실패하는 경우로 서버에 응답하지 않습니다.그것은 나타나는 비트의 일들이 벌어진 곳에서 생성되는 코드에서는 팩터리,하지만 나는 확실하지 않다.일반적인 코드의 검토는 또한 환영 합니다.:)

하단 라인,주변의 창조의 채널이나,시작하거나 비동기 결과는 대리인에서도/잡지 트랩 실패한 연결입니다.내가 좋아하는 캐치를 실행 ServiceCallError 이벤트입니다.

public class ServiceCaller : IDisposable
{
    private IFeedService _channel;

    public ServiceCaller()
    {
        var elements = new List<BindingElement>();
        elements.Add(new BinaryMessageEncodingBindingElement());
        elements.Add(new HttpTransportBindingElement());
        var binding = new CustomBinding(elements);
        var endpointAddress = new EndpointAddress(App.GetRootUrl() + "Feed.svc");
        _channel = new ChannelFactory<IFeedService>(binding, endpointAddress).CreateChannel();
    }

    public void MakeCall(DateTime lastTime, Dictionary<string, string> context)
    {
        AsyncCallback asyncCallBack = delegate(IAsyncResult result)
        {
            var items = ((IFeedService)result.AsyncState).EndGet(result);
            if (ItemsRetrieved != null)
                ItemsRetrieved(this, new ServiceCallerEventArgs(items));
        };
        _channel.BeginGet(lastTime, context, asyncCallBack, _channel);
    }

    public event ItemsRetrievedEventHandler ItemsRetrieved;
    public event ServiceCallErrorHandler ServiceCallError;

    public delegate void ItemsRetrievedEventHandler(object sender, ServiceCallerEventArgs e);

    public delegate void ServiceCallErrorHandler(object sender, ServiceCallErrorEventArgs e);

    public void Dispose()
    {
        _channel.Close();
        _channel.Dispose();
    }
}

여기에 스택 추적이,호기심이있는 사람들을 위해:

 An AsyncCallback threw an exception.
at System.ServiceModel.AsyncResult.Complete(Boolean completedSynchronously)
   at System.ServiceModel.AsyncResult.Complete(Boolean completedSynchronously, Exception exception)
   at System.ServiceModel.Channels.HttpChannelFactory.HttpRequestChannel.HttpChannelAsyncRequest.OnGetResponse(IAsyncResult result)
   at System.Net.Browser.BrowserHttpWebRequest.<>c__DisplayClassd.<InvokeGetResponseCallback>b__b(Object state2)
   at System.Threading._ThreadPoolWaitCallback.WaitCallback_Context(Object state)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Threading._ThreadPoolWaitCallback.PerformWaitCallbackInternal(_ThreadPoolWaitCallback tpWaitCallBack)
   at System.Threading._ThreadPoolWaitCallback.PerformWaitCallback(Object state)

그리고 이 목적을 이루기 위해서 나는 응용 프로그램을 불 브라우저에서 나는 죽일 웹 서버 프로세스에서는 Visual Studio.테스트 환경에서,나는 같은 일을 죽이고 네트워크 연결을 위해 클라이언트 시스템입니다.

여기에는 예외의 ToString():

System.Exception: An AsyncCallback threw an exception. ---> System.Exception: An AsyncCallback threw an exception. ---> System.ServiceModel.CommunicationException: The remote server returned an error: NotFound. ---> System.Net.WebException: The remote server returned an error: NotFound. ---> System.Net.WebException: The remote server returned an error: NotFound.
   at System.Net.Browser.BrowserHttpWebRequest.InternalEndGetResponse(IAsyncResult asyncResult)
   at System.Net.Browser.BrowserHttpWebRequest.<>c__DisplayClass5.<EndGetResponse>b__4(Object sendState)
   at System.Net.Browser.AsyncHelper.<>c__DisplayClass2.<BeginOnUI>b__0(Object sendState)
   --- End of inner exception stack trace ---
   at System.Net.Browser.AsyncHelper.BeginOnUI(SendOrPostCallback beginMethod, Object state)
   at System.Net.Browser.BrowserHttpWebRequest.EndGetResponse(IAsyncResult asyncResult)
   at System.ServiceModel.Channels.HttpChannelFactory.HttpRequestChannel.HttpChannelAsyncRequest.CompleteGetResponse(IAsyncResult result)
   --- End of inner exception stack trace ---
   at System.ServiceModel.Channels.Remoting.RealProxy.Invoke(Object[] args)
   at proxy_2.EndGet(IAsyncResult )
   at CoasterBuzz.Feed.Client.ServiceCaller.<MakeCall>b__0(IAsyncResult result)
   at System.ServiceModel.AsyncResult.Complete(Boolean completedSynchronously)
   --- End of inner exception stack trace ---
   at System.ServiceModel.AsyncResult.Complete(Boolean completedSynchronously)
   at System.ServiceModel.AsyncResult.Complete(Boolean completedSynchronously, Exception exception)
   at System.ServiceModel.Channels.ServiceChannel.SendAsyncResult.CallComplete(Boolean completedSynchronously, Exception exception)
   at System.ServiceModel.Channels.ServiceChannel.SendAsyncResult.FinishSend(IAsyncResult result, Boolean completedSynchronously)
   at System.ServiceModel.Channels.ServiceChannel.SendAsyncResult.SendCallback(IAsyncResult result)
   at System.ServiceModel.AsyncResult.Complete(Boolean completedSynchronously)
   --- End of inner exception stack trace ---
   at System.ServiceModel.AsyncResult.Complete(Boolean completedSynchronously)
   at System.ServiceModel.AsyncResult.Complete(Boolean completedSynchronously, Exception exception)
   at System.ServiceModel.Channels.HttpChannelFactory.HttpRequestChannel.HttpChannelAsyncRequest.OnGetResponse(IAsyncResult result)
   at System.Net.Browser.BrowserHttpWebRequest.<>c__DisplayClassd.<InvokeGetResponseCallback>b__b(Object state2)
   at System.Threading._ThreadPoolWaitCallback.WaitCallback_Context(Object state)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Threading._ThreadPoolWaitCallback.PerformWaitCallbackInternal(_ThreadPoolWaitCallback tpWaitCallBack)
   at System.Threading._ThreadPoolWaitCallback.PerformWaitCallback(Object state)

그런데,잡을 수있는 가장 쉬운 방법이 모두 응용 프로그램을 사용하는 수준 이벤트에 SL 클라이언트입니다.

도움이 되었습니까?

해결책

Jeff, 나는 당신의 질문에서 당신이 연결 고장을 어떻게 시뮬레이션하고 있는지 확실하지 않습니다. "서버가 응답하지 않는 것처럼"라고 가정하면 시간 초과 오류를 찾고 있다는 것을 의미합니다. 서버쪽에있는 Thread.Sleep ()를 사용하여 서비스 호출에서 느린 응답을 강요하여 응답하지 않는 서버를 시뮬레이션 할 수 있습니다. 오른쪽? 충분히 가깝습니까?

나는 비슷한 프로젝트를 가지고 있었기 때문에 이것을 테스트하고 타임 아웃과 다른 예외를 성공적으로 포착 할 수있었습니다. 사용자 정의 바인딩이 기본 시간 초과가 매우 길고 충분히 오래 기다리지 않았기 때문에 이러한 예외를 보지 못했을 것입니다.

설명하기 위해 WCF 타임 아웃은 바인딩 구성에 의해 제어됩니다. 코드를 살펴보면 다음과 같은 자신의 사용자 정의 바인딩을 작성합니다.

var elements = new List<BindingElement>();
elements.Add(new BinaryMessageEncodingBindingElement());
elements.Add(new HttpTransportBindingElement());
var binding = new CustomBinding(elements);

브레이크 포인트를 설정하고 만든 사용자 정의 바인딩을 검사하면 1 분이 걸립니다. SendTimeout 기본적으로. 나는 당신이 충분히 오래 기다리지 않았거나 서비스의 시뮬레이션 타임 아웃을 충분히 오래 설정하지 않았으므로 시간 초과 예외를 포착 할 수 없었습니다.

다음은 시연 할 코드 변경 사항이 있습니다. 먼저 바인딩에서 타임 아웃을 설정하려면 다음과 같습니다.

var binding = new CustomBinding(elements);
//Set the timeout to something reasonable for your service
//This will fail very quickly
binding.SendTimeout = TimeSpan.FromSeconds(1);

마지막으로, 예외를 포착하고 올바른 이벤트를 제기하려면 다음과 같이 AsyncCallback 대의원을 업데이트 할 수 있습니다. endget ()가 호출되면 예외가 발생합니다.

AsyncCallback asyncCallBack = delegate(IAsyncResult result)
{
    IFeedService service = ((IFeedService)result.AsyncState);
    try
    {
        var items = service.EndGet(result);
        ItemsRetrieved(this, EventArgs.Empty);
    }
    catch (System.TimeoutException ex)
    {
        //Handles timeout
        ServiceCallError(this, EventArgs.Empty);
    }
    catch (System.ServiceModel.CommunicationException ex)
    {
        //Handles a number of failures:
        //  Lack of cross-domain policy on target site
        //  Exception thrown by a service
        ServiceCallError(this, EventArgs.Empty);
    }
    catch (System.Exception ex)
    {
        //Handles any other errors here
        ServiceCallError(this, EventArgs.Empty);
    }
};

일반적인 코드 검토까지 바인딩 구성을 하드 코딩하지 않는 것이 좋습니다. 대신 ServiceReferences.clientConfig 파일에서 엔드 포인트 구성 (합리적인 시간 초과 포함)을 선언 할 수 있습니다.

new ChannelFactory<IFeedService>("feedServiceEndpoint");

그것은 시간이 지남에 따라 앱을 더욱 관리 할 수있게해야합니다.

이게 도움이 되길 바란다.

실내 변기

업데이트 :

제프,

여기서 일어나는 일에 대한 자세한 내용은이 코드와 의견을 살펴보십시오. makecall () 메소드는 beginget () 메소드로 전달되는 함수 (delegate)를 생성하는 것입니다. 이 기능은 나중에 서비스가 응답 할 때 실행됩니다.

"asynccallback asynccallback"및 "var 항목"으로 시작하는 줄에서 제안 된 변경 및 중단 점을 설정하십시오. 디버거를 통해 첫 번째 패스는 단지 서비스가 응답 할 때 실행하도록 코드 (함수/델리게이트)를 선언하고 두 번째 패스는 대의원 선언 내부에서 시작하여 해당 응답의 실제 처리입니다. 즉, 서비스 호출의 응답이 처리 될 때 외부 시도/캐치가 범위에 있지 않음을 의미합니다.

public void MakeCall(DateTime lastTime, Dictionary<string, string> context)
{
    try
    {
        AsyncCallback asyncCallBack = delegate(IAsyncResult result)
        {
            try
            {
                var items = ((IFeedService)result.AsyncState).EndGet(result);
                if (ItemsRetrieved != null)
                    ItemsRetrieved(this, new ServiceCallerEventArgs(items));
            }
            catch (Exception ex)
            { 
                //This will catch errors from the service call
            }
        };
        _channel.BeginGet(lastTime, context, asyncCallBack, _channel);
    }
    catch(Exception ex)
    {
        //This will not catch an error coming back from the service.  It will 
        //catch only errors in the act of calling the service asynchronously.

        //The communication with the service and response is handled on a different
        //thread, so this try/catch will be out of scope when that executes.

        //So, at this point in the execution, you have declared a delegate 
        //(actually an anonymous delegate the compiler will turn into a hidden class) 
        //which describes some code that will be executed when the service responds.

        //You can see this in action by setting a breakpoint just inside the delegate
        //at the line starting with "var items".  It will not be hit until the service
        // responds (or doesn't respond in a timeout situation).
    }
}

실내 변기

다른 팁

전에 비슷한 문제를 겪었습니다. 주요 문제는 프록시/채널의 인스턴스에서 idisposable이 구현되는 방식에 관한 것입니다. 내가 해결 한 방식은 아래 코드에 나와 있습니다. IDirector 내 서비스 계약입니다.

public class ProxyWrapper : IDisposable
{
    private IDirector proxy;
    private ChannelFactory<IDirector> factory;
    int callCount = 0;

    public ProxyWrapper()
    {
        factory = new ChannelFactory<IDirector>();

        proxy = factory.CreateChannel();
    }

    public IDirector Proxy
    {
        get
        {
            if (callCount > 0)
                throw new InvalidOperationException("The proxy can only be accessed once for every ProxyWrapper instance. You must create a new ProxyWrapper instance for each service request.");
            // only allow the proxy/channel to be used for one call.

            callCount++;
            return proxy;
        }
    }

    public void Dispose()
    {
        IClientChannel channel = (IClientChannel)proxy;

        try
        {
            if (channel.State != CommunicationState.Faulted)
            {
                channel.Close();
            }
            else
            {
                channel.Abort();
            }
        }
        catch (CommunicationException)
        {
            channel.Abort();
        }
        catch (TimeoutException)
        {
            channel.Abort();
        }
        catch (Exception)
        {
            channel.Abort();
            throw;
        }
        finally
        {
            channel = null;
            proxy = null;
        }
    }
}

위의 클래스를 사용하는 방법은 다음과 같습니다.

    public static void Login(string userName, string password)
    {
        using (ProxyWrapper wrapper = new ProxyWrapper())
        {
            currentSession = wrapper.Proxy.Login(userName, password);
        }
    }

때문에 ProxyWrapper 클래스 구현 IDisposable, 우리가 인스턴스를 사용하는 경우 ProxyWrapper 내부의 수업 using 블록, Dispose() 예외가 발생하더라도 방법은 호출됩니다. 코드 Dispose() 방법은 프록시/채널의 모든 사례와 상태를 처리합니다. 그런 다음이 메소드에서 오류 처리 / 로깅 / 이벤트 위임 코드를 추가 할 수 있습니다.

자세한 정보와 위의보다 일반적인 버전의 코드는 다음 블로그 항목을 읽으십시오. http://bloggingabout.net/blogs/erwyn/archive/2006/12/09/wcf-service-proxy-helper.aspx

서버로 통신 오류를 잡으려면 전화를 걸어 보는 것이 좋습니다. .CreateChannel() 시도해보십시오 ... CommunicationException 및 TimeOutexceptions와 같은 것을 잡아서 처리하십시오.

또한, 일반적인 조언 중 하나는 channelfactory를 만드는 것은 상당히 비싼 작업이므로이를 개별적으로 수행하고 어딘가에 ChanneFactory 객체를 참조하는 것을 저장할 수 있습니다.

ChannelFactory에서 채널을 만들고 다시 만들어야하는 경우, 채널을 저장 / 캐시 된 인스턴스를 항상 그대로 만들지 않고도 계속해서 사용할 수 있습니다.

그러나 그 외에는 당신이 여기서 꽤 잘하고 있다고 생각합니다!

마크

channelfactory 인스턴스는이 시간 초과가 발생할 때 "중단 된"상태에있을 것입니다. 통화에서 예외가 발생할 때가 아니라 _channel.dispose ()를 호출 할 때 예외가 발생한다는 것을 알 수 있습니다.

어떻게 든이 예외를 막아야합니다. 가장 간단한 방법은 처분 방법의 내용을 시도/잡는 것입니다. 그러나 닫기/폐기하려고하기 전에 _channel 인스턴스의 상태를 확인하면 더 좋습니다.

이것이 당신이 여전히 예외를 얻는 이유입니다. 여기서 처리되지 않습니다.

업데이트:

스택 추적을 기반으로 프레임 워크가 콜백 대의원을 실행하려고 시도하고 예외가 발생하는 것처럼 보입니다. 대의원의 내용을 시도/잡는 것은 문제를 해결하지 못한다고 다른 곳에서 응답했습니다 ... 내부 예외는 어떻습니까? exception.toString ()의 전체 출력은 무엇입니까?

나는 .NET 프레임 워크를 통과했으며 실제로 threadpool (적어도 여기에있는 스택 트레이스에서)으로 돌아갈 수 있으므로 처리되지 않은 예외와 스레드가 죽게됩니다. 더 나빠지면 나사산을 인수 할 수 있습니다 ... 나사산을 돌리며 내부는 내장 된 비동기 패턴을 사용하지 않고 서비스를 동기로 호출 할 수 있습니다 (이것이 WCF 듀플렉스 유형의 통신이 아니라면, 내가하지 않는 것입니다. t라고 생각합니다).

나는 여전히 전체 예외 정보 (더 많은 것이 있다면)가 유용 할 수 있다고 생각합니다.

Jeff,우리에게 보여주십시오 귀하의 코드하려고 할 때 try/을 잡을 차단위 EndGet 호출합니다.그것을 믿기 어렵지 않는 예외는 것을 보니,그것이 나에게 도움이 될 것입니다 믿습니다.

또한,이것의 일환으로 실험을 없애,이벤트 처리기입니다.내가 생각하기를 이 예외로 BrowserHttpRequest 실현가 404.내가 생각하는 매우 쉽고 간단하게 사용할 수 있는 이벤트,시/주변을 잡을 수있 EndGet 을 잡을 것 제외,if any.나는 당신을 따기 중간 상태의 예외 처리 프로세스.

나도 당신을 넣어 시스템입니다.디버거.휴식 또는 무언가가 바로 후 EndGet 호출합니다.

내가 당신이 당신의 ServiceCaller 당신이 그것을 호출 할 수 있도록 클래스 using 차단하다?

그렇다면 그게 문제입니다. WCF 채널 클래스는 가능한 모든 오류 조건을 충족시키는 완전히 일반적인 처분 코드를 작성하기가 매우 어렵다는 방식으로 설계되었습니다. 간단하다면 채널 클래스 자체는 처분하기에 안전하며 래퍼 클래스가 필요하지 않습니다.

내가 지금까지 찾은 유일한 안전한 방법은 처분 논리가 아니라 전체 통화 시퀀스를 캡슐화하는 것입니다.

public static void Invoke<TContract>(ChannelFactory<TContract> factory, Action<TContract> action) where TContract : class {

    var proxy = (IClientChannel) factory.CreateChannel();
    bool success = false;
    try {
        action((TContract) proxy);
        proxy.Close();
        success = true;
    } finally {
        if(!success) {
            proxy.Abort();
        }
    }
}

나는 그것이 동기 부름이라는 것을 알고 있지만 원칙은 동일합니다. 전화 해야하는지 추론하려고하는 대신 Close 또는 Abort, 당신은 당신이 그것을 넘어지기 전에 당신이 콜을 얼마나 멀리 얻었는지에 따라 미리 결정합니다.

참고 - 실제 채널만이 특별한 처리가 필요합니다. Afaik을 좋아하는 방식으로 공장을 처분 할 수 있습니다.

문제는 서비스 발신자를 설계 한 방식이라고 생각합니다.

서비스 발신자가 생성되면 채널이 열립니다. 이것은 채널이 타임 아웃 할 수 있고 시간이 지남에 따라 복구 할 수있는 코드가 없음을 의미합니다.

채널의 생성 및 폐쇄를 Make Call Method로 옮길 것입니다.

콜백의 내용을 시작하는 시작과 주변을 둘러 볼 수 있습니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top