0

「MTOM」および「ストリーミング」web.configパラメーターを使用して構築したWCF(非REST)ベースの非同期(非同期)バイナリファイルアップロードサービスを呼び出しています。環境はASP.NET MVC3、.NET4、IIS7.5、VS2010です。

このサービスは、zip、pdf、doc などのバイナリ ファイルをアップロードしようとします。非非同期アプローチでは、すべて正常に動作します。しかし、この非同期サービスを呼び出すと、クライアント側で次のエラー メッセージが表示されました。

パラメータhttp://tempuri.org/:dataStreamをシリアル化しようとしてエラーが発生しました。InnerException メッセージは、「Type 'System.IO.FileStream' with data contract name 'FileStream:http://schemas.datacontract.org/2004/07/System.IO' is not expected.」 でした。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);  
}

ストリームとメタ データ フィールドをラップするコンテナ クラス。例: fileName:

[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>
4

1 に答える 1

0

TPL とTaskFactory.FromAsyncメソッド ファミリを使用してみてください。

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

WCF はSystem.Stream型のシリアル化をサポートしていないようです。私には合理的に見えます。

于 2012-08-28T10:35:11.933 に答える