0

我正在调用基于 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>
4

1 回答 1

0

尝试使用 TPL 和TaskFactory.FromAsync方法族:

TaskFactory.FromAsync(BeginUpload, EndUpload, ...);
           .ContinueWith(result => ...);

似乎 WCF 不支持System.Stream类型的序列化。在我看来是合理的。

于 2012-08-28T10:35:11.933 回答