WebSurfer's Home

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

EDM にデータアノテーション属性を付与

by WebSurfer 2017年5月21日 15:10

Microsoft のチュートリアル「10 行でズバリ!! ASP.NET MVC を構成する各コンポーネントとネーミング ルール (C#)」のように、既存の SQL Server データベースから Entity Data Model (EDM) を生成して ASP.NET MVC アプリで使用する場合、ユーザー入力を検証するためのデータアノテーション属性をどのように付与できるかを書きます。

Visual Studio で生成した EDM

上の画像は、SQL Server 2008 Express のデータベース AdventureWorksLT をベースに、Visual Studio 2015 + EF6 で生成した EDM (Model1.edmx) です。(注:Visual Studio 2010 + EF4 で生成した EDM は内容が異なりますが、データアノテーション属性を付与する方法は同じです)

生成した EDM をベースに、上に紹介した「10 行でズバリ!!」チュートリアルと同様に、Visual Studio のスキャフォールディング機能を利用して、Address テーブルを表示・編集する ASP.NET MVC アプリの Controller と View を一式自動生成する場合を例に考えます。

自動生成された EDM で Address テーブルを表す Model のコードは、上の画像で示すように Address.cs にあります。そのコードは以下の通りです(説明に不要な部分は省略しています)。

namespace AdventureWorksLT
{
    using System;
    using System.Collections.Generic;
    
    public partial class Address
    {
        //・・・コンストラクタ(省略)・・・
        
        public int AddressID { get; set; }
        public string AddressLine1 { get; set; }
        public string AddressLine2 { get; set; }
        public string City { get; set; }
        public string StateProvince { get; set; }
        public string CountryRegion { get; set; }
        public string PostalCode { get; set; }
        public System.Guid rowguid { get; set; }
        public System.DateTime ModifiedDate { get; set; }
    
        //・・・ナビゲーションプロパティ(省略)・・・
    }
}

これに手を加えればよさそうに思えますが、それは NG です。何故なら、コードのコメントに書いてあるように、"このファイルを手動で変更すると、アプリケーションで予期しない動作が発生する可能性があります。このファイルに対する手動の変更は、コードが再生成されると上書きされます" ということだからです。

ではどうするかですが、TechNet の記事「[C#] #21. データ アノテーション検証コントロールでの検証」の「Entity Framework でのデータ アノテーション検証コントロールの使用」のセクションに書いてある方法を取るのがよさそうです。

TechNet の記事を読めばこれ以降の説明は不要かもしれませんが、記事がリンク切れになったりすると困るので、上の Address クラスの場合を例に方法を書いておきます。

具体的には以下のようなコードを EDM とは別の場所にクラスファイルを追加してそれに含めます。上の画像のソリューションエクスプローラの AddressMetaData.cs がそのクラスファイルです。

using System.ComponentModel;
using System.ComponentModel.DataAnnotations;

namespace AdventureWorksLT
{
    [MetadataType(typeof(AddressMetaData))]
    public partial class Address
    {

    }

    public class AddressMetaData
    {
        [Display(Name = "住所1")]
        [Required(ErrorMessage = "{0} は必須入力です")]
        public object AddressLine1 { get; set; }

        [Display(Name = "住所2")]
        public object AddressLine2 { get; set; }

        [Display(Name = "街")]
        [Required(ErrorMessage = "{0} は必須入力です")]
        [StringLength(10, ErrorMessage = "{0} は {1} 以内。")]
        public object City { get; set; }

        [Display(Name = "州")]
        [Required(ErrorMessage = "{0} は必須入力です")]
        public object StateProvince { get; set; }

        [Display(Name = "国名")]
        [Required(ErrorMessage = "{0} は必須入力です")]
        public object CountryRegion { get; set; }

        [Display(Name = "郵便番号")]
        [Required(ErrorMessage = "{0} は必須入力です")]
        public object PostalCode { get; set; }

        [Display(Name = "GUID")]
        [Required(ErrorMessage = "{0} は必須入力です")]
        public object rowguid { get; set; }

        [Display(Name = "更新日")]
        [Required(ErrorMessage = "{0} は必須入力です")]
        public object ModifiedDate { get; set; }
    }
}

上のコードで AddressMetaData が TechNet の記事で言うメタデータクラスです。それにプロキシプロパティを定義して必要なデータアノテーション属性を付与しています。

TechNet の記事にも書いてありますが、プロキシプロパティの型は自動生成された Address クラスのプロパティの型と同じである必要はないそうです(同じでも OK ですが)。上のコードでは TechNet の記事にならってプロキシプロパティの型は Object 型にしています。

自動生成された Address クラスは partial として定義されているところに注目してください。partial なので、上のコードのように別のクラスファイルに Address クラスを定義してそれに MetadataType 属性を付与することができます。(自動生成された Address クラスに手を加える必要はありません)

MetadataType 属性の引数に typeof(AddressMetaData) を設定することにより、メタデータクラス AddressMetaData を Address クラスに関連付けています。

以上の設定により、データアノテーション属性による検証が、クライアント側とサーバー側の両方で行われるようになります。

さらに、Display 属性も追加していますので、表示名が Display 属性の Name プロパティで設定した通りになります。

Tags: ,

MVC

LocalDB で Northwind と Pubs を利用

by WebSurfer 2017年5月16日 20:43

Microsoft が提供するサンプルデータベース Northwind と Pubs を LocalDB にアタッチして使うにはどうすればいいかということを書きます。

VS2015 から Pubs に接続

今年の 1 月に買った Windows 10 Pro 64-bit のノート PC に Visual Studio 2015 Communuty Update 3(以下、VS2015 と書きます)をインストールして使い始めました。

自分の環境では、VS2015 をインストールした際 SQL Server 2016 Express LocalDB(以下、LocalDB と書きます)も同時にインストールされたので、特に何もしていないのですが LocalDB が使えます。

VS2015 のテンプレートを使ってフォーム認証用の ASP.NET プロジェクトを作ると、ASP.NET Identity 用のデータベース(.mdf, .ldf ファイル)が EF Code First と LocalDB を使って App_Data フォルダに自動生成されるということで、LocalDB を使うのが VS2015 でのデフォルトのようです。

というわけで、少なくとも開発の初期段階では LocalDB を使うのが便利だと思って自分も使い始めました。

でも、実は最近になって、SQL Server Express が使えれば、LocalDB を使う必要はなさそうだと思い始めているのですが。LocalDB の使い方を勉強する時間がもったいないかも。(笑)

(上に書いた EF Code First で DB を生成するのは SQL Server Express でもできます。プロジェクトが生成された直後に web.config の接続文字列を SQL Server Express を使うように変更すれば、DB を生成して SQL Server Express の既定のインスタンス(または名前付きインスタンス)にアタッチしてくれます)

SQL Server Express が使えれば、LocalDB で Northwind と Pubs を使う必要はなさそうです。でも、せっかく考えたので書いておきます。この先 LocalDB しか使えないという環境もあるかもしれませんし。

さて、Northwind と Pubs ですが、それらは Microsoft が無償で提供している SQL Server 2000 用のサンプルデータベースです。MSDN ライブラリなどのチュートリアルでよく利用されており、自分もお世話になっています。

ファイルは Microsoft のダウンロードサイト Northwind and pubs Sample Databases for SQL Server 2000 から SQL2000SampleDb.msi というインストーラー形式で入手できます。

インストーラーを実行すると C:\SQL Server 2000 Sample Databases というフォルダ下に .mdf ファイル、.ldf ファイル、.sql ファイルが生成されます。

VS2015 から LocalDB を利用する場合、MSSQLLocalDB という名前の自動インスタンス(他に名前付きインスタンスというのもあるそうです)に特定の .mdf ファイル名を接続文字列の AttachDbFileName に指定してアタッチし、それに接続するのが一般的だと思います。

しかしながら、ダウンロードした NORTHWND.MDF や PUBS.MDF をアタッチしようとすると、SQL Server 2000 の .mdf ファイルなので SQL Server 2016 と互換性がないため、以下のようなエラーとなって失敗します。(この画像の前に「データベースを今すぐアップグレードしますか?」と出るので、そこで[はい]をクリックした結果です)

エラーメッセージ

ダウンロードした NORTHWND.MDF や PUBS.MDF を SQL Server 2016 と互換性を持つようにアップグレードできないかいろいろ調べましたが、自分が調べた限りでは方法は見つかりませんでした。

上の画像のエラーメッセージ "You must re-create the database" の通りデーターベースを作り直す他方法はなさそうです。

NORTHWND.MDF や PUBS.MDF と一緒にダウンロードされたスクリプトファイル instnwnd.sql(Northwind 用)と instpubs.sql(Pubs 用)を使えば作り直すことが可能です。問題はどのようにスクリプトを実行するかです。

実は知らなかったのですが、SQL Server Management Studio(以下、SSMS と書きます)を LocalDB に接続できるのでした。SSMS を利用すれば容易にスクリプトを実行できます。

下の画像は SQL Server 2008 の SSMS ですが、LocalDB に接続し、赤丸で囲った[ファイルを開く]アイコンをクリックして instnwnd.sql を開いたところです。

SSMS を LocalDB に接続

スクリプトには SQL Server 2016 では使用できないストアドプロシージャ sp_dboption が使われていますので、実行する前にその部分の修正が必要です(instnwnd.sql に 2 行、instpubs.sql に 1 行あります)。上の画像の赤の四角で囲った部分が instnwnd.sql の場合の訂正箇所です。

ストアドプロシージャの説明は MSDN ライブラリの sp_dboption (Transact-SQL) を見てください。チュートリアルに使うだけならコメントアウトするだけでもいいと思います。

同等の設定にするなら、alter database <データベース名> set recovery simple に書き換えればよさそうです。

訂正してから[実行(X)]をクリックするとスクリプトが走ってデーターベースが生成され、SSMS でデータベースを開いて操作できるようになります。

データベースが生成されると VS2015 からも接続できるようになります。下の画像は VS2015 のサーバーエクスプローラーからデータ接続の追加で、LocalDB 上に生成してアタッチされた Pubs に接続しているところです。

VS2015 を LocalDB に接続

上の画像で[データソース(S)]が既定のインスタンス(または名前付きインスタンス)に接続するためのものになっており、[サーバー名(E)]が LocalDB の MSSQLLocalDB という名前の自動インスタンスになっている点に注意してください。

それらが正しく選択されていれば[データベース名の選択または入力(D)]に上の画像のように接続可能な候補一覧が表示されるはずです。ここで Pubs を選択して[OK]をクリックした結果が一番上の画像です。

もちろんアプリケーションからも接続できます。既定のインスタンス(または名前付きインスタンス)に接続する時と同様な接続文字列で、Data Source を (LocalDB)\MSSQLLocalDB にすれば OK です。

Tags: , ,

DevelopmentTools

jQuery.ajax で JSONP

by WebSurfer 2017年5月3日 14:30

jQuery.ajax を使うと、JSONP(JSON with Padding・・・script タグを使用してクロスドメインでデータを取得する仕組み)を利用してデータを取得するのがかなり簡単にできるという話を書きます。

JSONP の応答を alert に表示

何が簡単になるかというと以下のことを jQuery.ajax が自動的にやってくれることです。

  1. jQuery.ajax を呼び出した時点で script タグを生成し DOM ツリーへ追加。(本来は、src 属性に呼出先の URL を設定した script タグを生成して DOM ツリーに追加するためのスクリプトを自力で書かなければなりません)
  2. 呼出先の URL にコールバックの名前を指定するクエリ文字列 &callback=jQuery... を追加。
  3. 応答が返ってくるとコールバックが呼び出され、引数に設定された JSON 文字列が JavaScript オブジェクトにパースされ、success: function (data) の data に渡される。

以下に具体例を書きます。MVC を例にとっていますが、もちろん Web Forms でも同様なことは可能です。

まず、ブラウザからの要求を受けて応答を返す窓口を作ります。MVC アプリの場合は、以下のようにアクションメソッドを使うのが簡単そうです。

具体的にどういう操作をしているかはコメントに書きましたのでそちらを見てください。

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity;
using System.Linq;
using System.Net;
using System.Web;
using System.Web.Mvc;
using AdventureWorksLT;
using System.Web.Script.Serialization;

namespace Mvc5App.Controllers
{
  public class AddressesController : Controller
  {
    // サンプルデータベース AdventureWorksLT をベースに
    // Visual Studio のウィザードで作った EDM を利用。
    // AdventureWorksLTEntities は DbContext を継承
    private AdventureWorksLTEntities db = 
                            new AdventureWorksLTEntities();

    // JSONP が呼び出すアクションメソッド
    public ActionResult Jsonp(int? id, string callback)
    {
      if (id == null)
      {
          return new HttpStatusCodeResult(
                                 HttpStatusCode.BadRequest);
      }

      // Address テーブルから指定された AddressID(主キー)
      // でレコードを検索。
      // 当該レコードの AddressID, AddressLine1, City フィー
      // ルドのデータを匿名クラスのオブジェクトとして取得
      var address = (from a in db.Address
         where a.AddressID == id
         select new { a.AddressID, a.AddressLine1, a.City }).
         FirstOrDefault();

      if (address == null)
      {
          return HttpNotFound();
      }

      // 取得したオブジェクトを JSON 文字列にシリアライズし
      // 変数 callback に名前が指定されたコールバックメソッ
      // ドの引数に設定。
      // code は「コールバック(JSON 文字列)」という文字列に
      // なる
      JavaScriptSerializer ser = new JavaScriptSerializer();
      string code = string.Format("{0}({1})", 
                          callback, ser.Serialize(address));

      // 文字列「コールバック(JSON 文字列)」を返す。その際
      // ヘッダに Content-Type: application/x-javascript; が
      // 付与される
      return JavaScript(code);
    }
    // ・・・中略・・・
  }
}

上記のアクションメソッドを、例えば /Addresses/Jsonp?id=25&callback=name で呼び出すと、以下の応答が返ってきます。

name({"AddressID":25,"AddressLine1":"9178 Jumping St.","City":"Dallas"})

呼び出し側(View)のコードは以下のようになります。上にも書きましたが、script 要素を DOM に追加するコードを書く必要はありません。以下のコードだけで jQuery.ajax が自動的にやってくれます。

<input type="text" id="tb1" />
<input type="button" value="検索" onclick="btn_click()" />

@section Scripts {
  <script type="text/javascript">
  //<![CDATA[
    function btn_click() {
      var address = '/Addresses/Jsonp?id=' + $("#tb1").val();

      $.ajax({
          url: address,
          dataType: 'jsonp',
          success: function (data) {
              alert(data.AddressID + ', ' +
                    data.AddressLine1 + ', ' + 
                    data.City);
          }
      });
    }
  //]]>
  </script>    
}

上記には jQuery の外部スクリプトファイルの参照が書いてありませんが、Visual Studio 2015 のテンプレートで自動生成した _Layout.cshtml を利用していますので、デフォルトで取り込まれるようになっています。

_Layout.cshtml を利用していますので、インラインのスクリプトは、上記のように View に @section Scripts { ... } を追加して、その中に書くのが適当です。header タグ内に書くと jQuery の外部スクリプトファイルが取り込まれる前になってしまいますので注意してください。

コールバックのメソッド名は、上にも書きましたが、jQuery.ajax がクエリ文字列として自動的に自動的にデフォルトの名前を追加しますので、それを利用しています。(デフォルトの名前では不都合がある場合は指定することも可能です)

data には、コールバックの引数の JSON 文字列が JavaScript オブジェクトに変換されて渡されます。なので、上記のようにプロパティを使ってデータを取得することができます。

Tags: ,

AJAX

About this blog

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

Calendar

<<  2017年5月  >>
30123456
78910111213
14151617181920
21222324252627
28293031123
45678910

View posts in large calendar