Dotnet跨应用程序域访问和需要特别注意的地方(WCF消息通道处于错误状态异常中的一种情况)
2012-02-06 19:34
549 查看
今天在做分布式计算原型的时候出现了WCF调用错误,错误大意是指消息通道处于错误状态,经过跟踪调试,发现是由于跨域调用的问题造成的.
问题场景是这样:客户端C通过WCF调用远程服务,该服务S启用一个新的应用程序域AD1,动态加载目标程序集,并执行该程序集中类CL的一个方法M,并将该方法的返回值(类型为T1)返回客户端C.这个应用程序域AD1虽然与主应用程序域都属于同一进程,但并不能直接相互访问,必须通过远程调用来实现,当然,由于都是同一个进程下,只要参与这个远程调用的对象都是继承自MarshalObjectByRef,这种远程调用并不需要象通常的那样通过remoting来实现,DotNet会自动处理。 Dotnet采用的是透明代理的方法(类似于我前面关于Spring技术博文中的AOP实现的方法),代理类是动态产生的。一般情况下这没有什么问题,但如果将调用目标方法返回的对象直接返回给WCF客户端,问题就出来了,就会报消息通道处于错误状态的异常。为什么会出现这种情况呢,原因如下:由于参与这个过程的类都会产生一个代理类,而且是动态产生的,因此调用方法M返回对象的实际类型并不是T1,而是T1 的一个代理子类TP1,注意,由于TP1是T1 的子类,基于面向对象继承的原则,父类可以用的地方,子类都可以用,因此编译时和运行时都不会出现类型错误。一般情况下这也没有问题,因为你使用T1类型变量是可以访问该代理对象的,但直接将该返回值对象作为WCF服务方法的返回值就有问题了,因为WCF对返回值进行序列化的时候是针对实际类型的(就是对象实例实际的类型,上面的调用中就是TP1),服务端用这个类型序列化没问题,因为类型TP1是存在的,但到客户端时,并不能反序列化回来,因为代理类TP1是动态产生的,只在服务端存在,客户端并不存在,所以就会报错。当然,这个错误报的有点笼统,害得我调试了半个小时才找到原因(分布式程序调式本身就是个细活)。
找到问题的原因,解决的办法很简单,就是重新创建一个返回类型的实例,并将跨域返回的类型的值克隆到这个实例,然后返回新建的这个实例对象即可。
这个错误比较隐蔽,主要原因是跨应用程序域访问的代理对象是DotNet动态产生的,而且是不需要程序员去干预的。
PS:这也是C#中协变和抗变时需要特别注意的地方.
补充:在自己增加WCF宿主服务时,一个端口只能被一个应用所有, 如果想在一台机器上启动多个宿主服务应用,则需进行端口级别的分离,路径和服务名只能在一个应用中做区分。否则会报该地址端口监听已被占用之类的的错误。而且由于一个服务需要建立一个host,因此建议一般情况下还是不要自己做宿主服务,用iis,WAS承载比较好。
补充(2012-02-08):跨域调用时参与的类需要从MarshalByRefObject继承,而这个类最好不要用于WCF通信参数或返回值,会发生莫名其妙的序列化错误(有时候可以,有时候又不可以,但错误率很高)。结论就是用于跨应用程序域访问的参与类不要参与WCF通信。如果实在要参与,就需要对这种类设置标准合同属性(DataContractattribute,DateMemberAttribute).就为了调试这个错误,浪费了我2个小时有多。感觉这是微软WCF的一个bug,要么嘛,你就可以,要么嘛你就不行,有时候序列化正常,有时候序列化又出错(出在客户端代理这边).
问题场景是这样:客户端C通过WCF调用远程服务,该服务S启用一个新的应用程序域AD1,动态加载目标程序集,并执行该程序集中类CL的一个方法M,并将该方法的返回值(类型为T1)返回客户端C.这个应用程序域AD1虽然与主应用程序域都属于同一进程,但并不能直接相互访问,必须通过远程调用来实现,当然,由于都是同一个进程下,只要参与这个远程调用的对象都是继承自MarshalObjectByRef,这种远程调用并不需要象通常的那样通过remoting来实现,DotNet会自动处理。 Dotnet采用的是透明代理的方法(类似于我前面关于Spring技术博文中的AOP实现的方法),代理类是动态产生的。一般情况下这没有什么问题,但如果将调用目标方法返回的对象直接返回给WCF客户端,问题就出来了,就会报消息通道处于错误状态的异常。为什么会出现这种情况呢,原因如下:由于参与这个过程的类都会产生一个代理类,而且是动态产生的,因此调用方法M返回对象的实际类型并不是T1,而是T1 的一个代理子类TP1,注意,由于TP1是T1 的子类,基于面向对象继承的原则,父类可以用的地方,子类都可以用,因此编译时和运行时都不会出现类型错误。一般情况下这也没有问题,因为你使用T1类型变量是可以访问该代理对象的,但直接将该返回值对象作为WCF服务方法的返回值就有问题了,因为WCF对返回值进行序列化的时候是针对实际类型的(就是对象实例实际的类型,上面的调用中就是TP1),服务端用这个类型序列化没问题,因为类型TP1是存在的,但到客户端时,并不能反序列化回来,因为代理类TP1是动态产生的,只在服务端存在,客户端并不存在,所以就会报错。当然,这个错误报的有点笼统,害得我调试了半个小时才找到原因(分布式程序调式本身就是个细活)。
找到问题的原因,解决的办法很简单,就是重新创建一个返回类型的实例,并将跨域返回的类型的值克隆到这个实例,然后返回新建的这个实例对象即可。
这个错误比较隐蔽,主要原因是跨应用程序域访问的代理对象是DotNet动态产生的,而且是不需要程序员去干预的。
PS:这也是C#中协变和抗变时需要特别注意的地方.
补充:在自己增加WCF宿主服务时,一个端口只能被一个应用所有, 如果想在一台机器上启动多个宿主服务应用,则需进行端口级别的分离,路径和服务名只能在一个应用中做区分。否则会报该地址端口监听已被占用之类的的错误。而且由于一个服务需要建立一个host,因此建议一般情况下还是不要自己做宿主服务,用iis,WAS承载比较好。
补充(2012-02-08):跨域调用时参与的类需要从MarshalByRefObject继承,而这个类最好不要用于WCF通信参数或返回值,会发生莫名其妙的序列化错误(有时候可以,有时候又不可以,但错误率很高)。结论就是用于跨应用程序域访问的参与类不要参与WCF通信。如果实在要参与,就需要对这种类设置标准合同属性(DataContractattribute,DateMemberAttribute).就为了调试这个错误,浪费了我2个小时有多。感觉这是微软WCF的一个bug,要么嘛,你就可以,要么嘛你就不行,有时候序列化正常,有时候序列化又出错(出在客户端代理这边).
相关文章推荐
- Dotnet跨应用程序域访问和需要特别注意的地方(WCF消息通道处于错误状态异常中的一种情况)
- JQuyer $.post 与 $.ajax 访问WCF ajax service 时的问题需要注意的地方
- DevOps2-在Jenkins容器中访问Gitlab容器需要特别注意的地方
- Unity协程(Coroutine)使用时需要注意的地方,协程异常中断,异常停止执行的一种可能性
- .net MVC 自定义异常错误页需要注意的地方
- android 外部调起要注意的地方——是否需要控制访问host
- JAVA中关于异常需要注意的地方
- 异常信息:CLR无法从COM 上下文0x645e18 转换为COM上下文0x645f88,这种状态已持续60秒。拥有目标上下文/单元的线程很有可能执行的是非泵式等待或者在不发送 Windows 消息的情况下处理一个运行时间非常长的操作.这种情况通常会影响到
- 重学C++Primer笔记5---一些基本语法需要特别注意的地方
- 初学WCF需要注意的地方
- IE8访问Fusion Middleware Control(Enterprise Manager)需要注意的地方
- (WCF)WCF开发需要注意的地方
- 从Check Point 官网使用Google Chrome 下载补丁需要特别注意的地方
- 有关异常需要理解和注意的地方!!!
- Windows的消息处理需要注意的地方
- .net下导致Session失效的一种情况:js教本中使用window.open和window.showModalDialog时需要注意
- .net下导致Session失效的一种情况:js教本中使用window.open和window.showModalDialog时需要注意
- java中long值表达式需要注意的一种情况
- React 特别需要注意的地方