异步调用MTOM流式文件上载时的序列化错误

时间:2012-08-28 10:24:37

标签: c# wcf asynchronous exception-handling asyncfileupload

我正在调用基于WCF(非REST)的异步(异步)二进制文件上传服务,我使用“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类型的单个param或MessageContract类型的单个param将被接受为服务方法中的类型。这种方法在非同步方式下工作正常。

为了全面了解细节我到目前为止提出了所有(相关的)代码部分:

服务器

服务合同接口,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作为Data成员嵌入的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服务标记文件:

   

的Web.config:

<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);
    }
}

App.config中:

<?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>

1 个答案:

答案 0 :(得分:0)

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

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

似乎WCF不支持System.Stream类型的序列化。对我来说看起来很合理。