我有一个WCF服务,允许不使用MessageContract上传文件。
[OperationContract, WebInvoke(UriTemplate = "UploadFile?filename={filename}")]
bool UploadFile(string filename, Stream fileContents);
我可以在Stream对象旁边使用另一个参数,因为它是UriTemplate的一部分。由于该服务作为托管Windows服务运行,我必须手动启动ServiceHost。
protected override void OnStart(string[] args)
{
FileServiceHost = new ServiceHost(typeof(FileService), new Uri("http://" + Environment.MachineName + ":8000/FileService"));
FileServiceHost.AddServiceEndpoint(typeof(IFile), new WebHttpBinding(), "").Behaviors.Add(new WebHttpBehavior());
FileServiceHost.Open();
}
有了所有这些,服务启动并正常工作。但是,我想将上面的一些内容移到app.config文件中。为此,我注释掉了OnStart
的第二行,并用FileServiceHost = new ServiceHost(typeof(FileService))
替换了第一行。然后我把这个信息添加到app.config…
<system.serviceModel>
<services>
<service name="Test.Server.FileService" behaviorConfiguration="DefaultBehavior">
<host>
<baseAddresses>
<add baseAddress="net.tcp://localhost:8000/FileService"/>
</baseAddresses>
</host>
<endpoint address="" binding="webHttpBinding" contract="IFile"/>
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="DefaultBehavior">
<serviceMetadata httpGetEnabled="true"/>
<serviceDebug includeExceptionDetailInFaults="true"/>
</behavior>
</serviceBehaviors>
</behaviors>
突然服务不能再启动。它在OnStart
方法的FileServiceHost.Open
上抛出了这个异常:"对于操作UploadFile中的请求是一个流,该操作必须有一个类型为流的参数。"
我在app.config中定义服务的方式一定有问题,因为当我从那里删除它时,一切都很好。我哪里做错了?
我是如何通过将webHttpBinding
添加到端点行为来解决这个问题的
将behaviorConfiguration="TestBehavior"
添加到<endpoint address="" binding="webHttpBinding" contract="IFile"/>
,然后定义TestBehavior
如下:
<endpointBehaviors>
<behavior name="TestBehavior">
<webHttp />
</behavior>
</endpointBehaviors>
要在WCF中启用流,有多个限制。其中之一是只有一个类型为Stream
的形参(或其他两种类型中的任何一种)
这可能意味着WCF"猜测"您正在尝试流式传输合同中的内容并将TransferMode
默认为Streamed
(这纯粹是一个猜测。这不是文献记载的。文档显示TransferMode
默认为Buffered
)
一个选项是在XML中显式地将传输模式设置为Buffered
:
<webHttpBinding>
<binding name="MyWebBinding" transferMode="Buffered"/>
</webHttpBinding>
然而,使用Buffered
传输模式,消息的内容在发送之前将被完全缓冲,这对于大文件来说不是一件好事。
另一个选择是使用Streamed
传输模式。如果你想流文件的内容,同时提供一个文件名,你必须定义一个自定义的Message
类,并在消息头中发送文件的元数据:
[MessageContract]
public class UploadFileMessage
{
[MessageHeader]
public string Filename { get; set; }
[MessageBodyMember]
public Stream Content { get; set; }
}