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