调用网络服务时出现未知异常

Unknown exception when calling a webservice

我的系统中有两个 WCF 网络服务。

网络服务A调用网络服务B。大部分时间,它工作正常。但是有时,当它调用 B 时会引发 A 中的异常:

Exception has been thrown by the target of an invocation.

为了想知道问题出在哪里,我在B中被调用的方法中,从第一条指令到最后一条指令都放了一个巨大的Try Catch。同样的问题。

然后我尝试将 customErrors 关闭并将 includeExceptionDetailInFaults 设置为 true,以便从异常中获取详细信息。同样的问题。

这不是超时问题,因为请求持续时间低于 1 秒。

我检查了请求的长度,一些好的请求比坏的请求长。问题不在于尺寸。

我无法重现问题,因为它只出现过几次。

我想如果问题出在 A 上,异常会比这条消息有更多的细节。

可能问题出在IIS上(都是在同一个IIS实例上),但是A通过localhost:xxxx/mywebservice.svc与B通信,所以很难相信这是一个通信问题。

编辑:

存在内部异常消息:

Cannot access a disposed object. Object name: 'System.ServiceModel.Channels.ServiceChannel'.

Web 服务 A 使用 B 的动态调用,使用:http://blogs.msdn.com/b/vipulmodi/archive/2006/11/16/dynamic-programming-with-wcf.aspx 另一个 link 在这里:https://github.com/carlosfigueira/WCFQuickSamples/tree/master/WCFForums/DynamicProxy

这是我的代码:

DynamicProxy proxy = null;
[...]
proxy = FactoryTest.getProxy(sServiceWsdl, sContract);
[...]
try {
   sXmlOUT = (String)proxy.CallMethod(sMethod, sXmlIN);
   proxy.Close();
catch (Exception e)
{
   // Here appears the exception
}
[...]

和工厂测试class:

public sealed class FactoryTest
{
    private static object syncRoot = new Object();
    private static Hashtable hashFactory = new Hashtable();

    public static DynamicProxy getProxy(String sServiceWsdl, String sContract)
    {
        if (hashFactory[sServiceWsdl] == null || ((ProxyTest)hashFactory[sServiceWsdl]).getTimeFromCreation().TotalSeconds > 60 * 60 * 6)
        {
            lock (syncRoot)
            {
                if (hashFactory[sServiceWsdl] == null || ((ProxyTest)hashFactory[sServiceWsdl]).getTimeFromCreation().TotalSeconds > 60 * 60 * 6)
                {
                    hashFactory.Add(sServiceWsdl, new ProxyTest(sServiceWsdl, sContract));
                }
            }
        }

        return ((ProxyTest)hashFactory[sServiceWsdl]).getProxy();
    }

    public static bool isProxyExists(String sServiceWsdl, String sContract)
    {
        lock (syncRoot)
        {
            return hashFactory[sServiceWsdl] == null ? false : true;
        }
    }
}

有一个完整的异常:

Exception has been thrown by the target of an invocation. at System.RuntimeMethodHandle._InvokeMethodFast(IRuntimeMethodInfo method, Object target, Object[] arguments, SignatureStruct& sig, MethodAttributes methodAttributes, RuntimeType typeOwner) at System.RuntimeMethodHandle.InvokeMethodFast(IRuntimeMethodInfo method, Object target, Object[] arguments, Signature sig, MethodAttributes methodAttributes, RuntimeType typeOwner) at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture, Boolean skipVisibilityChecks) at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
at System.RuntimeType.InvokeMember(String name, BindingFlags bindingFlags, Binder binder, Object target, Object[] providedArgs, ParameterModifier[] modifiers, CultureInfo culture, String[] namedParams) at WS_Generic.Service.CallWsMethod(String sXmlSettings, String sXmlIN, String& sXmlOUT)

有一个完整的 InnerException :

Cannot access a disposed object. Object name: 'System.ServiceModel.Channels.ServiceChannel'.

Server stack trace: at System.ServiceModel.Channels.CommunicationObject.ThrowIfDisposedOrNotOpen() at System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs, TimeSpan timeout) at System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(IMethodCallMessage methodCall, ProxyOperationRuntime operation) at System.ServiceModel.Channels.ServiceChannelProxy.Invoke(IMessage message)

Exception rethrown at [0]: at System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg) at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type) at IWsAfsol.EmvTrxAuthorization(String sXmlIn) at WsAfsolClient.EmvTrxAuthorization(String sXmlIn)

我快速 Google 浏览了您的更新,并在 asp.net 论坛上找到了 this post

我猜测(不知道您的代码是什么样子)您有一个共享 connection/service,您保持打开状态,然后在您的代码中需要时调用它。

选项(再次强调,没看代码就瞎猜)

  1. 调用前检查服务状态,看是否ready/able被调用。如果关闭,则创建一个新实例。 destroyed/created 的共享资源的问题是进程不是线程安全的,除非您使其成为线程安全的。
  2. (我会采取的方式)总是创建一个新实例并在每次调用时关闭它,让底层框架处理连接池。服务连接的范围仅在使用它的方法内。

根据您最近的编辑进行编辑:

在您的代码中,您正在调用 proxy.Close();这实际上是对 Dispose 的调用,但即使对象已被处置,您仍将其保留在哈希表中。您对同一个 object/service 的第二次调用(无论何时发生)都会触发您的异常。然后选项是 1. 仅在不再需要时调用 close/dispose,例如当应用程序关闭或完全在调用代码的上下文中使用短期实例时,即。为每次调用创建、使用、销毁和遗忘,并且不要将实例保存在哈希列表中(您可以将它变成一个构建器,为每个请求提供一个新实例)。

根据最新评论编辑 #2

对 Close 的调用仍然会破坏底层通道。您可以通过连续两次调用相同的服务并在第一次调用 Close() 后立即调用 GC.Collect() 来测试这一点,如下所示:

....
sXmlOUT = (String)proxy.CallMethod(sMethod, sXmlIN);
proxy.Close();
GC.Collect();
var sXmlOUT2 = (String)proxy.CallMethod(sMethod, sXmlIN); // should trigger your exception
...

这是一种重构代码的方法。

public class SomeClass
{
FactoryTest factory; // instantiate this somewhere like constructor

public void someMethod()
{
[...]
var proxy = factory.getProxy(sServiceWsdl, sContract);
[...]
try {
   sXmlOUT = (String)proxy.CallMethod(sMethod, sXmlIN);
   //  proxy.Close(); - do not close connection
catch (Exception e)
{
   // Here appears the exception
}
}// end method
}// end class

// optionally if you want the same instance to server other callers
// you can turn this into a Singleton pattern and return a shared 
// static instance of FactoryTest via a static method. Do not forget to 
// dispose of the singleton at the end of your application
public sealed class FactoryTest : IDisposable
{
    private object syncRoot = new Object();
    private Hashtable hashFactory = new Hashtable();

    public DynamicProxy getProxy(String sServiceWsdl, String sContract)
    {
        if (hashFactory[sServiceWsdl] == null || ((ProxyTest)hashFactory[sServiceWsdl]).getTimeFromCreation().TotalSeconds > 60 * 60 * 6)
        {
            lock (syncRoot)
            {
                if (hashFactory[sServiceWsdl] == null || ((ProxyTest)hashFactory[sServiceWsdl]).getTimeFromCreation().TotalSeconds > 60 * 60 * 6)
                {
                    hashFactory.Add(sServiceWsdl, new ProxyTest(sServiceWsdl, sContract));
                }
            }
        }

        return ((ProxyTest)hashFactory[sServiceWsdl]).getProxy();
    }

    public bool isProxyExists(String sServiceWsdl, String sContract)
    {
        lock (syncRoot)
        {
            return hashFactory[sServiceWsdl] == null ? false : true;
        }
    }

public void Dispose()
{
// implement IDisposable
// dispose of everything hashFactory using Close() or Dispose()
}

} // end class