WebSurfer's Home

トップ > Blog 1   |   Login
Filter by APML

HttpWebRequest で WCF サービスを呼出

by WebSurfer 26. March 2017 14:36

先の記事 WCF と jQuery AJAX では、JSON 文字列をデータとしてやり取りする WCF サービスのメソッドを、jQuery.ajax を使って呼び出してデータを取得する方法を書きました。

この WCF サービスのメソッドを HttpWebRequest / HttpWebResponse を利用して呼び出して JSON 文字列のデータを取得し、それを逆シリアル化して C# のオブジェクトに変換する方法を書きます。

ここでは例として先の記事の WCF サービスの GetCarsByDoors(int doors) メソッドを POST 要求してみます。

まず、JSON 文字列が逆シリアル化された際の C# のクラス / プロパティを書き、それらに DataContract / DataMember 属性を付与してデータコントラクトを定義します。

JSON 文字列から C# のクラス / プロパティの変換は json2csharp のような変換サービスを利用すると簡単にできると思います。

先の記事のコードでは、WCF サービスの GetCarsByDoors(int doors) メソッドの応答の JSON 文字列は "GetCarsByDoorsResult" でラップされるように設定されていますので、それを考慮して以下のようなデータコントラクト定義になります。

[DataContract]
public class RootObject
{
    // GetCarsByDoorsResult は WCF サービスメソッドに付与した
    // BodyStyle = WebMessageBodyStyle.WrappedRequest による
    // ラップの名前(ラップするのはセキュリティ対策)
    [DataMember]
    public List<Car> GetCarsByDoorsResult { get; set; }
}

[DataContract]
public class Car
{
    [DataMember]
    public string Make { get; set; }

    [DataMember]
    public string Model { get; set; }

    [DataMember]
    public int Year { get; set; }

    [DataMember]
    public int Doors { get; set; }

    [DataMember]
    public string Colour { get; set; }

    [DataMember]
    public float Price { get; set; }
}

HttpWebRequest を利用して WCF サービスの GetCarsByDoors(int doors) メソッドを要求し JSON 文字列をデータとして POST 送信します。ここでは例として "{\"doors\":5}" という 5 ドア車を要求する JSON 文字列を設定しています。

HttpWebResponse を利用して応答を取得し、DataContractJsonSerializer クラスを利用して応答ストリームに含まれる JSON 文字列を C# のオブジェクトにデシリアライズします。

詳しくは以下のサンプルコードとそれに付与したコメントを見てください。

using System;
using System.Net;
using System.Text;
using System.IO;
using System.Runtime.Serialization;
using System.Collections.Generic;
using System.Runtime.Serialization.Json;

namespace ConsoleApplication7
{
  class Program
  {
    static void Main(string[] args)
    {
      // 指定した Uri を要求する HttpWebRequest を初期化
      HttpWebRequest endpointRequest =
          (HttpWebRequest)HttpWebRequest.
          Create("http://.../carservice.svc/GetCarsByDoors");

      endpointRequest.Method = "POST";
      endpointRequest.ContentType = 
          "application/json; charset=utf-8";

      // POST データの設定。とりあえず 5 ドアを要求してみる
      string postData = "{\"doors\":5}";

      Encoding encoding = Encoding.GetEncoding("utf-8");
      byte[] byte1 = encoding.GetBytes(postData);
      endpointRequest.ContentLength = byte1.Length;

      // POST データを書き込むストリームを取得
      using (Stream requestStream = 
             endpointRequest.GetRequestStream())
      {
        // POST データを要求ストリームに書き込み
        requestStream.Write(byte1, 0, byte1.Length);

        // WCF サービスメソッドからの応答を取得
        using (HttpWebResponse endpointResponse =
               (HttpWebResponse)endpointRequest.GetResponse())
        {
          // 応答のコンテンツを読むストリームを取得
          using (Stream responseStream = 
                 endpointResponse.GetResponseStream())
          {
            // JSON シリアライザの初期化
            DataContractJsonSerializer ser = 
              new DataContractJsonSerializer(typeof(RootObject));

            // 応答のコンテンツを逆シリアル化して C# の
            // オブジェクトを取得
            RootObject rootObject = 
              (RootObject)ser.ReadObject(responseStream);
                        
            foreach (Car car in rootObject.GetCarsByDoorsResult)
            {
              Console.WriteLine("Make:{0}, Model:{1}, Doors:{2}",
                                car.Make, car.Model, car.Doors);

            /*
            結果は:
            Make:Audi, Model:A4, Doors:5
            Make:Ford, Model:Focus, Doors:5
            Make:Renault, Model:Laguna, Doors:5
            Make:Toyota, Model:Previa, Doors:5
            */

            }
          }
        }
      }
    }
  }
}

DataContractJsonSerializer クラスを利用したシリアル化 / 逆シリアル化については、MSDN ライブラリの記事「方法 : JSON データをシリアル化および逆シリアル化する」が参考になると思います。

Tags: ,

.NET Framework

Chart Samples

by WebSurfer 14. February 2016 16:15

先の記事 Chart で ASP.NET Web Forms アプリでグラフを表示するのに便利な Chart コントロールを紹介しましたが、MSDN のサイトからサンプルを入手できるので、入手方法その他備忘録として残しておいたほうがよさそうなことを書いておきます。

Chart Samples

まず入手先ですが、以下のページから Windows Forms アプリ用と ASP.NET Web Forms アプリ用のサンプルをダウンロードできます。

Samples Environments for Microsoft Chart Controls

Windows Forms 用のサンプルは上のページで[C# (5.6MB)]をクリック、ASP.NET Web Forms アプリ用なら[HTML (4.4MB)]の方をクリックしてください。両方入手したいなら[Full (10.0MB)]をクリックしてください。

「Samples Environments for Microsoft Chart Controls.zip」という名前の zip ファイルの中に、Windows Forms 用なら「Windows Forms Samples Environment for Microsoft Chart Controls\C#」というフォルダがあって、それに完全な Windows Forms アプリのソリューションとしてサンプルが含まれています。

ASP.NET Web Forms アプリ用なら「ASP.NET Samples Environment for Microsoft Chart Controls\HTML」というフォルダがあって、それに完全な ASP.NET Web Forms アプリの Web サイトプロジェクトとしてサンプルが含まれています。

それを適当なフォルダに解凍して Visual Studio(.NET4 なので 2010 以降のバージョン)で開いて実行すれば、基本の解説、いろいろなタイプのサンプルのデモ、それを作るための C# および VB.NET サンプルコード等が満載のアプリが動くはずです。

上の画像は Windows Forms アプリ用のサンプルを実行したときのものです。ASP.NET Web Forms アプリ用は同様なサンプルがブラウザ上に表示されます。

解説が英語であるのを厭わなければこのサンプルは非常に有益なものになると思います。というか、このサンプル無しでは Chart を使っての開発は難しいと言っても過言でなないと思います。

一つだけ注意事項があります。Windows Forms アプリのサンプルは、ソリューションのフォルダ名が zip を解凍したときのままですと以下の画像のように C# と言う名前になっており、これをそのままソリューションのフォルダとして使う場合は、フォルダ名 C# を CSharp などに書き換えないとうまく動かないということです。(最後が # で終わるのは不可)

フォルダ構成

ちょっと余計な話ですが、何故 '#' があるとダメなのかも調べたので、以下にその理由も書いておきます。

プログラムの中で Application.ExecutablePath プロパティを利用してパス名を得ていますが、.exe ファイルの出力パスが .\ と設定されているので、フォルダ名が C# のままですと得られるパス名の文字列は "C:\ ... \\Windows Forms Samples Environment for Microsoft Chart Controls\\C#/WinFormsChartSamples.exe" となってしまいます。(C# の直後が '\\' ではなくて '/' であることに注意)

その後、上記文字列からファイル名を除去するのに applicationPath.LastIndexOf('\\'); でインデックスを取得しているのですが、'\\' を期待しているところが '/' になっているのでうまく行きません。

C# を CSharp などに書き換えると "C:\ ... \\Windows Forms Samples Environment for Microsoft Chart Controls\\CSharp\\WinFormsChartSamples.exe" となって期待通りに行きます。

何故 C# の直後が '\\' ではなくて '/' なってしまうかと言うと Application.ExecutablePath のコードで '#' が fragment と見なされるからだそうです。

その詳細は stackoverflow の記事 Odd C# path issue にありますので興味があれば見てください。(手抜きですみません)

Tags:

.NET Framework

C# と VB.NET のフィールド初期化子の違い

by WebSurfer 6. February 2016 15:25

C# でフィールドをインスタンスメソッド等で初期化しようとすると「フィールド初期化子は、静的でないフィールド、メソッド、またはプロパティ 'xxx' を参照できません」(xxx はそのメソッド名)というコンパイルエラーになります。

ところが VB.NET ではコンパイラは問題なく通ってしまいます。実行上も、自分が調べた限りですが、期待通り動きました。(ただし、詳しく調べたわけではないので、どんなケースでも問題ないかは分かりませんが)

理由は、想像ですが、C# と VB.NET のコンパイラの違いによるものだと思われます。

(1) Why Do Initializers Run In The Opposite Order As Constructors? Part One、(2) Can VB.NET be forced to initialize instance variables BEFORE invoking the base type constructor?、(3) Field initializer differences between C# and VB.NET 等の記事を読んでみると:

  • C# の場合: まず最初に全ての initializers が derived ⇒ base の順で実行され、次に全ての constructor bodies が base ⇒ derived の順で実行される。
  • VB.NET の場合: constructor bodies が base ⇒ derived の順で実行される。その際、各 initializer は当該 constructor body が実行される直前に実行される。

という違いがあるようです。

知ってました? 実は自分はつい最近まで知らなかったです。(汗) 知っておくべきところは以上なんですが、それだけではブログの記事として面白くないので、無知な自分がハマった話を書いておきます。(笑)

その話は、以下のような VB.NET のコードを C# に書き換えるということから始まりました。

Public Class Sample
    Public Property Code As Integer()

    Public ReadOnly Property Codes As IList(Of Integer)
        Get
            Return Me._Codes.Value
        End Get
    End Property
    
    Private ReadOnly _Codes As New Lazy(Of IList(Of Integer))(
        Function()
            Return If(Me.Code, {}).ToList()
        End Function)
End Class

VB.NET をよく知らない自分は Telerik Code Converter などの変換サービスを利用しています。

自動変換されたコードをそのまま信じて使うのは問題ありと認識していますが、かなりのところまで変換してくれるので多少の手直しで済み、重宝しています。

今回の場合では、VB.NET のコードの If(Me.Code, {}) のところで {} は変換してくれなかったので、そこのみ new int[0] に直して Visual Studio のエディタに貼り付けました。(VB.NET のコードで {} は New Integer() {} と見なされるらしいです。実は、それも知らなかったのですが、さすがに C# で {} ではダメなのは気がつきましたので直しました)

そうすると、以下の画像の赤丸で示した部分でエラーが出ます。

Visual Studio に表示されたエラー

エラーメッセージは最初の方は:「ラムダ式 はデリゲート型ではないため、型 'System.Threading.LazyThreadSafetyMode' に変換できません」(コンパイルした時のエラーメッセージは「ラムダ式 はデリゲート型ではないため、型 'bool' に変換できません。」になります)

2 つ目は:「キーワード 'this' は現在のコンテキストでは使用できません」

となりました。それらのエラーメッセージから原因は分かりますでしょうか? 無知な自分は最初原因が分からず、半日ぐらいハマってしまいました。(汗)

結局原因は、Code がインスタンスプロパティなので(静的ではないので)それを使ってフィールドを初期化することはできないということだったんですが、それなら「フィールド初期化子は、静的でないフィールド、メソッド、またはプロパティ 'Code' を参照できません」というエラーメッセージを出して欲しかったです。(泣き言)

解決策は、変数 _Codes をコンストラクタで初期化するのがよさそうです。(Code プロパティに static キーワードを付与して静的プロパティにするのは止めた方がよさそうです)

Tags:

.NET Framework

About this blog

2010年5月にこのブログを立ち上げました。その後 ブログ2 を追加し、ここは ASP.NET 関係のトピックス、ブログ2はそれ以外のトピックスに分けました。

Calendar

<<  September 2020  >>
MoTuWeThFrSaSu
31123456
78910111213
14151617181920
21222324252627
2829301234
567891011

View posts in large calendar