文章目录
原因
春节在家,有一点时间,想着那将自己这半年的对于WCF使用进行一个总结,希望自己这个春节不要懒癌发作,借此在这里先里一个flag。也希望对后来的人学习有一定的帮助。(今天写这篇文章也是第一次使用MarkDown编辑器,很多功能也不了解。希望可以借此学习到一些新的知识。)
关于使用wsDualHttpBinding出现的问题
当服务端部署在其他电脑上,连接不上的问题
当我将服务部署在本地的时候,可以执行,但是我将其部署在其他电脑上的时候,却出现连接超时的问题。解决的方法是添加clientBaseAddres,添加位置是如下
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<bindings>
<wsDualHttpBinding>
<binding name="" clientBaseAddress="http://本机IP:8888/" />
</wsDualHttpBinding>
</bindings>
<client>
<endpoint address="" binding="wsDualHttpBinding"
bindingConfiguration="" contract=""
name="" />
</client>
原因是为什么我其实也不是特别的了解,当我寄宿在IIS的时候,这个方法可以解决。但是我后来寄宿在Windows应用程序的时候,这个方法也不太顶用,不过我觉得下面这段话应该有参考价值。
回调的服务监听地址采用的默认端口是80,在IIS 5.x以及之前的版本中,80端口是IIS独占的监听端口。所以才会出现AddressAlreadyInUseException异常并提示地址被另外一个应用使用,实际上80端口被IIS使用…由于问题的症结在于回调服务的监听端口和IIS冲突,所以我们只要能够解决这种冲突,就能从根本上解决这个问题。由于我们不可以为了解决这个问题把IIS卸掉,或者改变IIS默认的端口,所以我们只能改变回调服务的地址。WsDualHttpBinding定义了一个ClientBaseAddress使你能很容易地改变回调服务的基地址。对于我们给出的案例,我们只要通过下面的配置将clientBaseAddress设为可用的地址
如果想用windowsj寄宿程序寄宿服务的话,我建议修改成netTcpBinding,就不需要修改ClientBaseAddress了
用WCF进行上传下载传输数据过大的问题
只需要增加上传下载的容量就行。客户端,服务端都需要这样设置。
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<bindings>
<wsDualHttpBinding>
<binding name="WSDualHttpBinding_IBaseHelperService" maxReceivedMessageSize="2147483647" maxBufferPoolSize="2147483647" messageEncoding="Mtom" receiveTimeout="00:50:00">
<readerQuotas maxArrayLength="2147483647" maxStringContentLength="2147483647" maxBytesPerRead="2147483647" />
<security mode="None">
</security>
</binding>
</wsDualHttpBinding>
</bindings>
<client>
<endpoint address="" binding="wsDualHttpBinding"
bindingConfiguration="" contract=""
name="" />
</client>
关于WCF服务端执行代码的问题
我有段时间将本地执行代码迁移到服务端,其中System.Windows.Forms.Application.DoEvents();这个在服务端是没有用到代码,在一些情况下,对我执行的结果造成了影响。所以如果是在本地执行而改到服务端不用代码,必须要清除。
注意事项
wsDualHttpBinding回调的参数修改时候一定要注意更新,否则报错信息真的蛮难理解的。
关于WCF与ARCGIS结合使用的问题
ArcEngine在服务端执行速度减慢
我在工作中有遇到,在服务端执行ArcEngine,发现,与本地执行相比,很多时候速度会慢很多,这就很难办,后来发现是ArcEngine多线程的问题。将wcf服务部署执行的时候,本质是打开一个线程,在线程中进行ArcEngine操作。因此ArcEngine的执行是多线程的。而ArcEngine 组件都被标记为单线程单元。每个STA都限制在一个线程中,但是COM并不限制每个进程中STA的数目。当一个方法调用进入一个STA,它被转移到STA的唯一线程。因此,在STA中的一个对象将一次只接收和处理一个方法调用,它接收的每个方法调用会到达同一线程。
微软开发人员网络(MSDN)网站上有提到:“新的线程就初始化为ApartmentState.MTA。主应用程序线程默认初始化为ApartmentState.MTA。“而wcf服务打开的线程就默认是MTA, 这意味着,如果您的应用程序不被视为一个单一线程应用程序初始化的,.NET框架将为所有的ArcObjects创建一个特殊的单线程单元(STA)线程,因为他们被标记STA。这将导致对每一个从应用程序调用ArcObjects的线程切换到这个特定的线程上来。反过来,这迫使ArcObjects组件合在一起调用,并最终以COM组件调用可能慢了约50倍。而该方法可以有效的解决这个问题。
我们发现下方的代码可以实现线程从MTA转到STA:
// An highlighted block
Thread thread = new Thread( new ThreadStart( StartService ) );
thread.IsBackground = false;
thread.SetApartmentState( ApartmentState.STA );
thread.Start();
thread.GetApartmentState();
但是,问题又来了,在WCF中,线程的生成,不可以由上方的代码那样执行的,因为线程早就已经打开了。后来发现,可以从配置方面下手。
在执行的方法上面添加属性,便可以进行更改,提高速度。
先写一个类,STAOperationBehaviorAttribute继承Attribute, IoperationBehavior,
STAOperationInvoker 继承 IOperationInvoker。具体代码如下:
// An highlighted block
class STAOperationBehaviorAttribute : Attribute, IOperationBehavior
{
public void AddBindingParameters(OperationDescription operationDescription,
System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
{
}
public void ApplyClientBehavior(OperationDescription operationDescription,
System.ServiceModel.Dispatcher.ClientOperation clientOperation)
{
// If this is applied on the client, well, it just doesn’t make sense.
// Don’t throw in case this attribute was applied on the contract
// instead of the implementation.
}
public void ApplyDispatchBehavior(OperationDescription operationDescription,
System.ServiceModel.Dispatcher.DispatchOperation dispatchOperation)
{
// Change the IOperationInvoker for this operation.
dispatchOperation.Invoker = new STAOperationInvoker(dispatchOperation.Invoker);
}
public void Validate(OperationDescription operationDescription)
{
if (operationDescription.SyncMethod == null)
{
throw new InvalidOperationException("The STAOperationBehaviorAttribute " +
"only works for synchronous method invocations.");
}
}
}
public class STAOperationInvoker : IOperationInvoker
{
IOperationInvoker _innerInvoker;
public STAOperationInvoker(IOperationInvoker invoker)
{
_innerInvoker = invoker;
}
public object[] AllocateInputs()
{
return _innerInvoker.AllocateInputs();
}
public object Invoke(object instance, object[] inputs, out object[] outputs)
{
// Create a new, STA thread
object[] staOutputs = null;
object retval = null;
Thread thread = new Thread(
delegate()
{
retval = _innerInvoker.Invoke(instance, inputs, out staOutputs);
}
);
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
thread.Join();
outputs = staOutputs;
return retval;
}
public IAsyncResult InvokeBegin(object instance, object[] inputs,
AsyncCallback callback, object state)
{
// We don’t handle async…
throw new NotImplementedException();
}
public object InvokeEnd(object instance, out object[] outputs, IAsyncResult result)
{
// We don’t handle async…
throw new NotImplementedException();
}
public bool IsSynchronous
{
get { return true;
}
}
}
然后在WCf服务端添加行为,使其每次获得请求就重新生成一个线程执行。
[ServiceBehavior(InstanceContextMode=InstanceContextMode.PerSession,UseSynchronizationContext = false)]
最后到要实现的方法前添加特性,[STAOperationBehavior]
待续
参考文章:
链接: [link](http://www.cnblogs.com/artech/archive/2007/03/02/661969.html.
链接: [link](https://scottseely.com/2009/07/17/calling-an-sta-com-object-from-a-wcf-operation/.