3

大きなストリームを受け入れて処理し、そのストリームを返す WCF サービス操作が必要です。

必要なものへの参照として、 Large Data Streaming に関する MSDN の記事を使用しました。私はその記事のアドバイスに従いました。

事前に質問する:

  1. コントラクトで指定したときに、生成されたサービス操作に戻り値の型がない理由を知りたいですか?

  2. それが予期される動作である場合、ストリームを渡して処理されたストリームを返すにはどうすればよいですか?

詳細

入力ストリームと戻りストリームの両方に MetaData を添付する必要があるため、必要に応じてクラスを MessageContract 属性で装飾しました。

これが私の実装の簡単な概要です。

メッセージ コントラクト:

[MessageContract]
public class InputStreamMessage
{
    [MessageHeader]
    public InputStreamHeader Header { get; set; }

    [MessageBodyMember(Order = 1)]
    public Stream Data { get; set; }

}

[MessageContract]
public class OutputStreamMessage
{
    [MessageHeader]
    public OutputStreamHeader Header { get; set; }

    [MessageBodyMember(Order = 1)]
    public Stream Data { get; set; }

}

サービス契約:

[ServiceContract]
public interface IStreamService
{
    [OperationContract]
    OutputStreamMessage ProcessStream(InputStreamMessage input);
}

サービスの実装:

 public OutputStreamMessage DoStreamOperation(InputStreamMessage input)
 {
    //Some logic that assigns re
    OutputStreamMessage output = DoSomeNonBufferedProcessing(input);

    return output;
 }

クライアント側:

次に、クライアント側でサービス参照を生成し、以下のようにサービスを呼び出します。

private void PerformStreamOperation()
{
    try
    {
        //
        StreamServiceReference.StreamServiceClient client = new StreamServiceReference.StreamServiceReferenceClient();
        client.Open();

        //Set Header and Parameters
        InputMessageHeader header = new InputMessageHeader();

        //...                
        //... initialize header data here
        //...                

        //... do some operation to get input stream
        var inputstream = SomeOperationToGetInputStream();

        //Perform Service stream action
        //         ____ [ Why does the generated method have the following signature, retuning void?]
        //        |     [ If this is expected, how do I use it? ]
        //        |
        //        V 
        client.DoStreamOperation(header, ref inputstream); 


        //...                
        //... Do what you wish with data
        //...                

    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message.ToString(), "Stream Processing Error");
    }
}

MSDN の記事では、公式の WCF サンプルに存在するものとまったく同じコントラクトを使用しています。

Stream EchoStream(ストリームデータ)

しかし、同等の MessageContract 実装の例はありません。サンプル バージョンは期待どおりの結果をもたらします。

アップデート

  • サービス参照に、予期されるメソッド シグネチャで生成された Task/Asynchronous メソッドがあることに気付きました。おそらくこれは、Stream プロパティで MessageContract を使用し、同様に構造化されたオブジェクトを返す場合、それを非同期で呼び出す必要があることを意味します。どこにも文書化されているのを見たことがありません。メソッドを使用してみます -同期操作が必要なため、機能しませんでした。
  • ChannelFactoryまた、生成されたプロキシ クライアントの代わりに を使用してみました。

      EndpointAddress endpoint = new EndpointAddress("net.tcp://localhost:9910/StreamService");
    
      channelFactory = new ChannelFactory<IStreamService>("netTcpStreamedEndPoint");
      channelFactory.Endpoint.Contract.SessionMode = SessionMode.Allowed;
      IStreamService service = channelFactory.CreateChannel();
    
4

1 に答える 1

2

回答に返信して申し訳ありません(コメントに対する評判はありません)。

私はあなたと同じようなプロジェクトに取り組んでいます-大量のデータストリームを(MessageContractsを使用して)受け入れ、それを処理し、クライアントがこれらのデータをダウンロードできるサービスがあります。

まず第一に - 入力パラメータ:

 client.DoStreamOperation(header, ref inputstream); 

MessageContracts を含むサービス プロキシを生成していないようです ( http://blogs.msdn.com/b/zainnab/archive/2008/05/13/windows-communication-foundation-wcf-what-theを参照)。 -hell-is-always-generate-message-contracts.aspx )。これにより、クライアント側で OutputStreamMessage および InputStreamMessage コントラクトが提供されます。

messageContracts が適切に生成されたので、コンパイル エラーを受け取ることなく、これらの両方をコードに記述できます。

client.DoStreamOperation(inputStreamMessage)

   StreamServiceReference.StreamServiceClient.OutputStreamMessage outputMessage = client.DoStreamOperation(inputStreamMessage)

しかし、基本的に最初のものは役に立ちません。もちろん、最初に InputStreamMessage オブジェクトを作成する必要があります。

StreamServiceReference.StreamServiceClient.InputStreamMessage inputStreamMessage = new StreamServiceReference.StreamServiceClient.InputStreamMessage();

ご希望であれば、MessageContracts のサンプルを投稿できます。

また、次の記事もご覧ください: http://www.codeproject.com/Articles/166763/WCF-Streaming-Upload-Download-Files-Over-HTTP。私のメッセージコントラクは、プロジェクトの初期段階では似ていました


EDIT:セッションモードは次のように設定されています:

[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Multiple)]

その理由は、複数のクライアントに共通するオブジェクトに関する情報 (状態) を維持する必要があるためです。ただし、これはストリーミングには影響しません。

これが私のバインディングです。基本的なhttpを使用しています:

      <basicHttpBinding>
        <binding name="TransferBinding" transferMode="Streamed" maxReceivedMessageSize="10067108864">
        </binding>
      </basicHttpBinding>

アップロードのために、私はこの種のメッセージコントラクトを使用しています:

    [MessageContract]
        public class RemoteFileInfo : IDisposable
        {
            [MessageHeader(MustUnderstand = true)]
            public string FileName;

            [MessageBodyMember]
            public System.IO.Stream FileByteStream;
}

これは、サービス側で定義された StartUpload() を呼び出す、クライアント側で定義されたメソッドの本体です (アップロードするファイルにつながる filePath を定義する必要があります)。

using (System.IO.FileStream stream = new System.IO.FileStream(filePath, FileMode.Open, FileAccess.Read))
        {
                           // start service client
                CalculationServiceClient client = new CalculationServiceClient();     

                RemoteFileInfo remoteFileInfo = new RemoteFileInfo(); ;
                remoteFileInfo.FileName = TextBox1.Text;
                remoteFileInfo.FileByteStream = stream;

                // upload file
                client.StartUpload(remoteFileInfo);

                // close service client
                client.Close();
                uploadStream.Close();
            }
        }

次に、サービス側で StartUpload() operationContract を定義します。StartUpload コントラクトの内部は、たとえば次のようになります。

public void StartUpload(RemoteFileInfo fileInfo)
        {

            string filePath = define your filePath, where you want to save the file;           

            int chunkSize = 2048;
            byte[] buffer = new byte[chunkSize];

            using (System.IO.FileStream writeStream = new System.IO.FileStream(filePath, System.IO.FileMode.CreateNew, System.IO.FileAccess.Write))
            {
                do
                {
                    // read bytes from input stream (provided by client)
                    int bytesRead =  fileInfo.FileByteStream.Read(buffer, 0, chunkSize);
                    if (bytesRead == 0) break;

                    // write bytes to output stream
                    writeStream.Write(buffer, 0, bytesRead);
                } while (true);

                writeStream.Close();
            }
        }
于 2015-03-16T13:57:07.630 に答える