调用网络服务时出现未知异常
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,您保持打开状态,然后在您的代码中需要时调用它。
选项(再次强调,没看代码就瞎猜)
- 调用前检查服务状态,看是否ready/able被调用。如果关闭,则创建一个新实例。 destroyed/created 的共享资源的问题是进程不是线程安全的,除非您使其成为线程安全的。
- (我会采取的方式)总是创建一个新实例并在每次调用时关闭它,让底层框架处理连接池。服务连接的范围仅在使用它的方法内。
根据您最近的编辑进行编辑:
在您的代码中,您正在调用 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
我的系统中有两个 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,您保持打开状态,然后在您的代码中需要时调用它。
选项(再次强调,没看代码就瞎猜)
- 调用前检查服务状态,看是否ready/able被调用。如果关闭,则创建一个新实例。 destroyed/created 的共享资源的问题是进程不是线程安全的,除非您使其成为线程安全的。
- (我会采取的方式)总是创建一个新实例并在每次调用时关闭它,让底层框架处理连接池。服务连接的范围仅在使用它的方法内。
根据您最近的编辑进行编辑:
在您的代码中,您正在调用 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