我正在调用基于 WCF(非 REST)的异步(async)二进制文件上传服务,该服务是我使用“MTOM”和“Streamed”web.config 参数构建的。环境是 ASP.NET MVC 3、.NET 4、IIS 7.5、VS2010。
该服务尝试上传二进制文件,例如 zip、pdf 或 doc。使用非异步方法一切正常。但是当我调用这个异步服务时,我在客户端收到了这个错误消息:
尝试序列化参数http://tempuri.org/:dataStream时出错。InnerException 消息是 'Type 'System.IO.FileStream' ,数据合同名称为 'FileStream:http://schemas.datacontract.org/2004/07/System.IO' 不是预期的。考虑使用 DataContractResolver 或将任何静态未知的类型添加到已知类型列表中 - 例如,通过使用 KnownTypeAttribute 属性或将它们添加到传递给 DataContractSerializer 的已知类型列表中。有关更多详细信息,请参阅 InnerException。
由于异步方法,我必须重写有关流的 WCF 约束:Stream 类型的单个参数或 MessageContract 类型的单个参数将被接受为服务方法中的类型。这种方法在以非异步方式工作时效果很好。
为了获得详细信息,我将展示到目前为止所有(相关)代码部分:
服务器:
服务合约接口,IUploadService:
[ServiceContract]
public interface IUploadService
{
[OperationContract(AsyncPattern = true)]
IAsyncResult BeginUpload(CompletedAsyncUploadResult dataStream, AsyncCallback callback, object state);
int EndUpload(IAsyncResult result);
}
包装流和元数据字段的容器类,例如文件名:
[DataContract]
[KnownType(typeof(Stream))]
public class StreamUploadContainer
{
[DataMember]
public string fileName;
[DataMember]
public Stream content;
}
将 StreamUploadContainer 作为数据成员嵌入的 IAsyncResult 派生类:
// Simple async result implementation.
public class CompletedAsyncUploadResult : IAsyncResult
{
private StreamUploadContainer data;
public StreamUploadContainer Data
{
get { return data; }
set { data = value; }
}
public object AsyncState
{ get { return (object)data; } }
public WaitHandle AsyncWaitHandle
{ get { throw new Exception("The method or operation is not implemented."); } }
public bool CompletedSynchronously
{ get { return true; } }
public bool IsCompleted
{ get { return true; } }
}
服务类 UploadService.svc:
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)]
public class UploadService : IUploadService
{
public IAsyncResult BeginUpload(CompletedAsyncUploadResult dataStream, AsyncCallback callback, object state)
{
string retVal = "All went ok.";
try
{
string docFolderPath = Path.Combine(HostingEnvironment.ApplicationPhysicalPath, @"App_Data/Scanning/");
using (FileStream outputStream = File.Create(Path.Combine(docFolderPath, dataStream.Data.fileName)))
{
dataStream.Data.content.CopyTo(outputStream);
}
dataStream.Data.content.Close();
}
catch (Exception)
{
dataStream.Data.content.Close();
retVal = "Somethin went wrong.";
}
CompletedAsyncUploadResult retValContract = new CompletedAsyncUploadResult();
retValContract.Data = new StreamUploadContainer();
retValContract.Data.fileName = retVal;
retValContract.Data.content = Stream.Null;
return retValContract;
}
public int EndUpload(IAsyncResult result)
{
return 0;
}
}
WCF 服务标记文件:
网络配置:
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="BasicHttpBinding_IUploadService" messageEncoding="Mtom" transferMode="Streamed" maxReceivedMessageSize="2147483647">
<security mode="None">
<transport clientCredentialType="None" />
</security>
</binding>
</basicHttpBinding>
</bindings>
<services>
<service name="WcfUploadServiceProject.WcfServices.FileStreamUpload.UploadService" behaviorConfiguration="defaultBehaviour">
<endpoint binding="basicHttpBinding" contract="WcfUploadServiceProject.WcfServices.FileStreamUpload.IUploadService" bindingConfiguration="BasicHttpBinding_IUploadService"></endpoint>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="defaultBehaviour">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true"/>
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
客户:
异步调用服务
private void Upload()
{
UploadBinaryService.IUploadService client = new UploadBinaryService.UploadServiceClient();
UploadBinaryService.StreamUploadContainer contract = new UploadBinaryService.StreamUploadContainer();
contract.content = this.GetStreamSample(@"c:\temp\balloon.zip");
contract.fileName = "balloon.zip";
UploadBinaryService.CompletedAsyncUploadResult result = new UploadBinaryService.CompletedAsyncUploadResult();
result.Data = contract;
client.BeginUpload(result, FileStreamBinaryUploadAsyncServiceCallback, client);
}
public void FileStreamBinaryUploadAsyncServiceCallback(IAsyncResult result)
{
var proxy = result.AsyncState as UploadBinaryService.IUploadService;
if (proxy != null)
{
var value = proxy.EndUpload(result);
}
}
应用程序配置:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="BasicHttpBinding_IUploadService" closeTimeout="00:01:00" openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00" allowCookies="false" bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard" maxBufferSize="65536" maxBufferPoolSize="524288" maxReceivedMessageSize="65536" messageEncoding="Mtom" textEncoding="utf-8" transferMode="Streamed" useDefaultWebProxy="true">
<readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384" maxBytesPerRead="4096" maxNameTableCharCount="16384" />
<security mode="None">
<transport clientCredentialType="None" proxyCredentialType="None" realm="" />
<message clientCredentialType="UserName" algorithmSuite="Default" />
</security>
</binding>
</basicHttpBinding>
</bindings>
<endpoint address="http://example.com/WcfServices/FileStreamUpload/UploadService.svc" binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_IUploadService" contract="UploadBinaryService.IUploadService" name="BasicHttpBinding_IUploadService" />
</client>
</system.serviceModel>
</configuration>