WebSurfer's Home

トップ > Blog 1   |   ログイン
APMLフィルター

VS2015 でユーザーインスタンス利用

by WebSurfer 2018年9月24日 13:57

SQL Server Express のユーザーインスタンスを利用して、Visual Studio Community 2015 の TableAdapter 構成ウィザードで型付 DataSet + TableAdapter を自動生成する際の注意点を書いておきます。

Visual Studio 2010 Professional と同じ手順で作業すると、作業の過程で以下のように、"ファイル xxx.mdf の自動的に名前が付けられたデータベースをアタッチできませんでした。同じ名前のデータベースが既に存在するか、指定されたファイルを開けないか、UNC 共有に配置されています。" というエラーが出て先に進めません。

エラーメッセージ

原因はここまでの作業で生成された接続文字列に User Instance=True が付与されないからです。

接続文字列には AttachDbFilename でアタッチする .mdf ファイルは指定されるのですが、その場合 User Instance=True で明示的にユーザーインスタンスを使うようにする必要があるようです。

(想像ですが、User Instance=True が付与されていないので、既定の(または名前付き)インスタンスにアタッチしようとして、権限の問題で失敗していると思われます。先の記事「DB のアタッチ」参照)

TableAdapter 構成ウィザードで作業を進めていく際、「接続の追加」ダイアログで[詳細設定(V)...]ボタンをクリックすると「詳細プロパティ」ダイアログが表示されますので、そこでユーザーインスタンスを使用するように設定する必要があります。

詳細プロパティ

「詳細プロパティ」で[Data Source]を LocalDB から SQL Server Express に変更するところは気が付きましたが、もう一つ[User Instance]がデフォルトで False になっていて、これを True に変更する必要があることに気が付きませんでした。

ちなみに、Visual Studio 2010 Professional では「詳細プロパティ」の[User Instance]は True に設定されています。

「詳細プロパティ」の設定後、先に進んで TableAdapter 構成ウィザードに戻ったときに接続文字列を確認できます。

接続文字列

上の画像のように User Instance=True となっていれば OK です。後は従前の手順で進めていけば問題なく型付 DataSet + TableAdapter が完成するはずです。

ユーザーインスタンスはすでに非推奨になっていて、最近の開発には LocalDB を利用するので、このような問題に悩むケースはほとんどなさそうですが、忘れないよう備忘録として残しておくことにしました。(笑)

Tags: ,

ADO.NET

ASP.NET Web API のバインディング

by WebSurfer 2018年9月11日 16:52

ASP.NET Web API でもクライアントから送信されてきたデータはアクションメソッドの引数にバインドされますが、その仕組みは ASP.NET MVC とは異なるということを書きます。

ASP.NET Web API

詳しくは MSDN Blog の記事「How WebAPI does Parameter Binding」に書いてありますのでそちらを読んでもらうとして、要点だけ書きますと以下の通りです。

  1. Model Binding または Formatters を利用するという 2 つの方法があって、Web API の場合は、クエリ文字列からパラメータを取得する場合は Model Binding を、ボディから取得する場合は Formatter を使う。
  2. string などのプリミティブ型をアクションメソッドの引数にした場合、引数に属性が付与されてなければクエリ文字列からパラメータを探してモデルバインディングする。
  3. コンプレックス型(例:記事の Customer クラス、下のコードの Hero クラス)の場合、デフォルトでは Formatter を使ってボディからパラメータを取得する。

上記の点を踏まえたサンプルコードを以下にアップしておきます。

Model

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Runtime.Serialization;

namespace WebAPI.Models
{
    public class Hero
    {
        public int Id { get; set; }
        public string Name { get; set; }
    }
}

Api Controller

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using System.Web;
using WebAPI.Models;
 
namespace WebAPI.Controllers
{
    public class ValuesController : ApiController
    {
        private List<Hero> heroes = new List<Hero> {
              new Hero {Id = 1, Name = "スーパーマン"},
              new Hero {Id = 2, Name = "バットマン"},
              new Hero {Id = 3, Name = "ウェブマトリクスマン"},
              new Hero {Id = 4, Name = "チャッカマン"},
              new Hero {Id = 5, Name = "スライムマン"}
          };

        // GET api/values (Read...すべてのレコードを取得)
        public List<Hero> Get()
        {
            return heroes;
        }

        // GET api/values/5 (Read...id 指定のレコード取得)
        public Hero Get(int id)
        {
            return heroes[id - 1];
        }

        // POST api/values  (Create...レコード追加)
        public List<Hero> Post(Hero postedHero)
        {
            heroes.Add(postedHero);
            return heroes;
        }

        // PUT api/values/5 (Update...id 指定のレコード更新)
        public List<Hero> Put(int id, [FromBody] string name)
        {
            heroes[id - 1].Name = name;
            return heroes;
        }

        // DELETE api/values/5 (Delete...id 指定のレコード削除)
        public List<Hero> Delete(int id)
        {
            heroes.RemoveAt(id - 1);
            return heroes;
        }
    }
}

上の Api を呼び出すスクリプト

<input type="button" value="READ ALL"
       onclick="apiHeroesGet();" />
<input type="button" value="READ 5"
       onclick="apiHeroesGet5();" />
<input type="button" value="UPDATE 5"
       onclick="apiHeroesPut5();" />
<input type="button" value="DELETE 5"
       onclick="apiHeroesDelete5();" />
<input type="button" value="CREATE 6"
       onclick="apiHeroesPost();" />

<ul id="heroes"></ul>

@section Scripts {
  <script type="text/javascript">
  //<![CDATA[

    function apiHeroesGet() {
      $.ajax({
        type: "GET",
        url: "api/values",
        success: function (data, textStatus, jqXHR) {
          $('#heroes').empty();
          $.each(data, function (key, val) {
            var str = val.Id + ': ' + val.Name;
            $('<li/>', { html: str }).appendTo($('#heroes'));
          });
        },
        error: function (jqXHR, textStatus, errorThrown) {
          $('#heroes').empty();
          $('#heroes').text('textStatus: ' + textStatus +
              ', errorThrown: ' + errorThrown);
        }
      });
    }

    function apiHeroesGet5() {
      $.ajax({
        type: "GET",
        url: "api/values/5",
        success: function (data, textStatus, jqXHR) {
          $('#heroes').empty();
          var str = data.Id + ': ' + data.Name;
          $('<li/>', { html: str }).appendTo($('#heroes'));
        },
        error: function (jqXHR, textStatus, errorThrown) {
          $('#heroes').empty();
          $('#heroes').text('textStatus: ' + textStatus +
              ', errorThrown: ' + errorThrown);
        }
      });
    }

    function apiHeroesPut5() {
      $.ajax({
        type: "PUT",
        url: "api/values/5",
        data: encodeURI("=ガッチャマン"),

        // 以下のコードがあるとバインドできず null が渡される
        //contentType: "application/json; charset=utf-8",

        success: function (data) {
          $('#heroes').empty();
          $.each(data, function (key, val) {
            var str = val.Id + ': ' + val.Name;
            $('<li/>', { html: str }).appendTo($('#heroes'));
          });
        },
        error: function (jqXHR, textStatus, errorThrown) {
          $('#heroes').empty();
          $('#heroes').text('textStatus: ' + textStatus +
             ', errorThrown: ' + errorThrown);
        }
      });
    }

    function apiHeroesDelete5() {
      $.ajax({
        type: "DELETE",
        url: "api/values/5",
        success: function (data) {
          $('#heroes').empty();
          $.each(data, function (key, val) {
            var str = val.Id + ': ' + val.Name;
            $('<li/>', { html: str }).appendTo($('#heroes'));
          });
        },
        error: function (jqXHR, textStatus, errorThrown) {
          $('#heroes').empty();
          $('#heroes').text('textStatus: ' + textStatus +
              ', errorThrown: ' + errorThrown);
        }
      });
    }

    function apiHeroesPost() {
      var j = { Id: 6, Name: "ガッチャマンの息子" };
      var jsonString = JSON.stringify(j);
      $.ajax({
        type: "POST",
        url: "api/values",
        data: jsonString,
        contentType: "application/json; charset=utf-8",
        success: function (data) {
          $('#heroes').empty();
          $.each(data, function (key, val) {
            var str = val.Id + ': ' + val.Name;
            $('<li/>', { html: str }).appendTo($('#heroes'));
          });
        },
        error: function (jqXHR, textStatus, errorThrown) {
          $('#heroes').empty();
          $('#heroes').text('textStatus: ' + textStatus +
              ', errorThrown: ' + errorThrown);
        }
      });
    }

  //]]>
  </script>
}

特に注意すべき点は Put(int id, [FromBody] string name) メソッドとそれを呼び出すスクリプトです。

アクションメソッドの引数が string 型でボディからパラメータを得たい場合は [FromBody] 属性が必要です。

さらに、name にバインドするのに Formatter がどういう動きをしているのか調べ切れてませんが、自分が試した限りでは送信するデータをスクリプトの data に "name=value" と設定してはダメで、上のサンプルコードのように "=value" というように設定しないと引数 name にはバインドされませんでした。

もう一つ、Content-Type に application/json を設定するとバインドできず null が渡されてしまいます。理由は、Web API は要求ヘッダの Content-Type を見て適切な Formatter を選択するそうですが、"=value" は JSON ではないからだと思われます。

jQuery ajax で contentType を設定しないと application/x-www-form-urlencoded (デフォルト) となりますが、その場合は期待通り引数 name に "value" がバインドされます。

上記のようなトラブルを避けるために、アクションメソッドの引数は Post(Hero postedHero) と同様にコンプレックス型(Hero クラス)とし、スクリプトのapiHeroesPost() のように JSON 文字列を送信するようにした方が良いかもしれません。

Tags: ,

MVC

IIS Express で SSL 通信

by WebSurfer 2018年9月9日 14:12

Visual Studio Community 2015 で ASP.NET Web アプリケーションの開発を行う際、IIS Express で SSL 通信を利用できるようにする方法を書きます。

IE11 の実行画面

上の画面は、Visual Studio での設定完了後、Web Forms アプリケーションを IIS Express 上で実行させて IE11 に表示させたものです。

赤枠で囲ったアドレスバーに示される URL が https で始まっているのが分かるでしょうか? IE11 と Edge の場合は上の画像のように警告なしで表示されます。

そのための Visual Studio での設定方法は以下の通りです。

プロジェクトのプロパティ設定

Visual Studio のテンプレートを使って Web アプリケーションのプロジェクトを作成したら、ソリューションエクスプローラーでプロジェクトのノードをクリックしてプロパティを表示します。

プロパティウィンドウで[SSL 有効]を True に設定します。すると、[SSL URL]に自動的に SSL 通信用のプロジェクトの URL が設定されます。上の画像を見てください。

その操作を行う際に警告ダイアログが出て(正確なタイミングとダイアログの内容は忘れました)、自動的にフレンドリ名 IIS Express Development Certificate というサーバー証明書が発行されます。

サーバー証明書

上の画像で赤枠で囲ったものが発行されたサーバー証明書です。発行場所は上の画像の通り「現在のユーザー」です。MMC で確認する場合は、スナップインを追加する際[ユーザーアカウント(M)]を選んでください。

サーバー証明書なので発行されるのは最初の一回だけです。この後、新たに別のプロジェクトを作って同じ操作を行ってもダイアログは出ませんし証明書は発行されませんので注意してください。

仮想ディレクトリの作成

Visual Studio のソリューションエクスプローラーで Properties を右クリックして開きます。表示される画面で[Web]タブをクリックし、上の画像のように[プロジェクトの URL(J)]のテキストボックスに、プロジェクトのプロパティウィンドウの[SSL URL]に設定された URL をコピーします。

その後[仮想ディレクトリの作成(Y)]ボタンをクリックすると「仮想ディレクトリは正しく作成されました」というダイアログが出ます。

binding の設定

作成された仮想ディレクトリは applicationHost.config ファイルの binding タグを見ると確認できます。上の画像の赤枠部分を見てください。

applicationHost.config ファイルはプロジェクトのフォルダにあります。詳しくは先の記事「ApplicationHost.config の場所」を見てください。

以上の設定後、[ファイル(F)]⇒[すべて保存(L)]してから、[デバッグ(D)]⇒[デバッグの開始(S)]または[デバッグなしで開始(H)]で、上の画像のように既定のブラウザが立ち上がって SSL 通信で要求・応答が行われ、結果が表示されます。

Chrome の実行画面

Chrome などの他のブラウザでも、上の画像のような警告は出ますが、Visual Studio を使って IIS Express で SSL 通信を行っての開発は可能なようです。

全てのブラウザで、IE11、Edge を使った場合と同様か、100% 問題ないかはまでは確認していませんが。

Tags: ,

DevelopmentTools

About this blog

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

Calendar

<<  2018年11月  >>
28293031123
45678910
11121314151617
18192021222324
2526272829301
2345678

View posts in large calendar