Archive

Archive for the ‘ASP.NET MVC 3’ Category

コードファーストでSQL Serverを利用する接続文字列

コードファーストの説明ではこのエントリのようにSQL CE 4.0を利用するものが多いです。
雑誌の記事を書くにあたってSQL Serverを利用する方法を試行錯誤したのでまとめておきます。

1.接続文字列を書かない

接続文字列を記述していない場合は、デフォルトでSQL Server Expressのインスタンスが利用されます。
SQL Server Expressがインストールされていないマシンではエラーが発生すると思われます(未確認)。
このとき、データベース名はDbContextを継承したクラスのnamespaceを含んだ名前になります。
例えば"MvcApplication13.Models.MemberDB"というデータベースが作られたりします。

2.データベース名を指定するには

データベース名を指定するにはDbContextを継承したクラスと同じ名前の接続文字列を追加します。
このときInitial Catalogで指定した名前のデータベースが、指定したSQL Serverのインスタンスの中に作られます。
次のような記述であれば、MemberDBという名前のデータベースが作成されます。

例:
    <add name="MemberDB"
         connectionString="data Source=.\SQLEXPRESS;Integrated Security=SSPI;Initial Catalog=MemberDB;"
         providerName="System.Data.SqlClient" />

 

3.ユーザーインスタンスを利用するには

一般的な開発ではSQL Server Express特有の機能であるユーザーインスタンスを利用して、データベースのmdfファイルをプロジェクトの内部に作成して利用しています。
このように作成するデータベースのmdfファイルをプロジェクトの中に含めたい場合、ユーザーインスタンスを利用するための設定とmdfファイルの配置場所を設定する必要があります。
これは次のような記述で実現できます。

例:
    <add name="MemberDB"
         connectionString="data Source=.\SQLEXPRESS;Integrated Security=SSPI;AttachDbFilename=|DataDirectory|MemberDB.mdf;User Instance=True;Initial Catalog=MemberDB;"
         providerName="System.Data.SqlClient" />

この場合mdfファイルが作られても最初はソリューションエクスプローラの中に表示されませんが、すべてのファイルを表示させてmdfファイルをプロジェクトに含めれば、あとは通常の開発時と同様に扱えます。

ただし、ユーザーインスタンスを使っているときに注意が必要な点が一つあります。

  『データモデルを変更したからといってmdfファイルを削除しないこと』

適切なInitializerの設定をしていれば、モデルの変更時にデータベースを自動的に作り変えてくれます。
この機能を使わなくてもmdfファイルを消せば新しくデータベースが作られるのではないか、と思ってしまいがちですがそんなことはありません。
むしろよくわからないエラーがでてきて対処に困る、という状況におちいります(というかこれでハマりました)。

どうもmdfファイルを直接消してしまうとユーザーインスタンス側になんらかのゴミが残るようです。
もしおかしな状態になってしまったら、次のような処理でユーザーインスタンスの状態をクリアしてください。

1)PCを再起動する
2)再起動後、すぐにSQL Server Expressのサービスを停止する
3)以下のフォルダを削除する
      C:\Users\ログインユーザー名\AppData\Local\Microsoft\Microsoft SQL Server Data\SQLEXPRESS

ASP.NET MVC 3 Remote Validation で検証タイミングを遅らせる

2011年11月25日 1件のコメント

Lazy Remote Validation with ASP.NET MVC 3

Remote Validation って JQuery Validator の仕様の関係でキー入力ごとに走っちゃうんですね。
この対応方法は覚えておかなくちゃ。

まぁ、Remote Validation を含め、クライアントでの検証をどう使うかまだ自分の中できちんとしたガイドライン作れてないんですが。
業務アプリだとサーバーサイドでないと確認できない検証って絶対あると思ってるんですが、そこでクライアント側での検証も利用しちゃうと検証エラーの発生するタイミングが2回でてくるんで、入力する人からみてうっとおしいって思われるんじゃないかと。
そのあたり、実際にはどう使っていくのがよいか考えきれてないです。。。

カテゴリー:.NET, ASP.NET MVC 3

ASP.NET MVC 3 での Unobtrusive ベースの検証の仕組み

DataAnnotations, MVC 3, and Unobtrusive Validations

ASP.NET MVC 3 ではクライアント側で検証が行われる際、控えめなJavaScriptと呼ばれる形式を利用するようになっています。
このあたりについて、ソースコードのどこをみたらよいか、といったところからはじめて仕組みを解説してくれる記事がありました。
なかなか興味深いです。

カテゴリー:.NET, ASP.NET MVC 3

ASP.NET MVC で1画面にボタンが2つあるときの処理

まずは一般的な方法。

http://www.dotnetcurry.com/ShowArticle.aspx?ID=705

ボタンをクリックしたとき、ボタンのnameをkeyとしてvalueの値をもつデータが Request.From の中に上がってきます。
って、これ常識、でいいですよね?
なので、アクションの引数にボタンのnameと同じ名前の引数をもってれば、そこに値が入っているかどうかでどのボタンがおされたか判断できることになります。
このあたりはモデルバインディングがうまくやってくれるとこです。

ここまでは私もすぐ気づいて、実際に使ってるんですよね。
ただ、もうちょっとスッキリというかカッコよく書けないか、とは思ってたところ。。。

http://www.dotnetcurry.com/ShowArticle.aspx?ID=724

同じ人の投稿ですが。
ActionMethodSelectorAttributeをうまく使うと、それぞれのボタンクリック時に別のアクションを呼び出せるようになりますよ、と。
いやぁ、目からウロコです。
ASP.NET MVCっていろんなとこで拡張できる、って話は聞いてましたが、こんなことできるんですねぇ。

まだまだ勉強が足りん。。。

カテゴリー:.NET, ASP.NET MVC 3

ASP.NET MVC モデルバインディングの基礎

Understanding ASP.NET MVC Model Binding

ASP.NET MVCで使えるモデルバインディングは非常に強力な機能です。
アクションメソッドの引数を適切に設定してあげれば、勝手に値が設定されていくんですから。。。
そのモデルバインディングがどのようなものなのか、詳細を理解するのによい記事がありました。
標準のモデルバインディングの仕組みを拡張して独自のバインディングを組み込むところまで説明されていますから、こんなにいい記事はありません。

ということで、のちのち何度も参考にするだろうからメモ(w

カテゴリー:.NET, ASP.NET MVC 3

mvcConf @:Japan ビデオ公開

mvcConf @:Japan ~ ASP.NET MVC ブートキャンプ ~

mvcConf @:Japan の各セッションおよび会場で流れたビデオ等が公開されました。
なんと公開先はChannel9です。
いいのかなぁ。。。

私のセッションについては以下のページで見ることができます。
まぁ、セッションのビデオは画面の映像だけなのがちょっと安心できるとこ(w

http://channel9.msdn.com/Events/mvcConf/Japan/mvcConf-Japan/Session4

セッションは45分と短い時間だったため、実はBlogに書いた内容のほうが詳しくなっています。
それでもなんなので、ほんとに一部だけBlogに書いていない話もあったりしますが。

よろしかったら見てやってください。

#自分ははずかしいので見れない気がする。。。

mvcConf @:Japan ふりかえり 6: Webサービス(OData)連携

2011年6月22日 1件のコメント

.NET Frameworkを利用している場合、WCF Data Servicesを利用することでODataベースのWebサービスを簡単に提供することができます。
このWCF Data Services と連携するASP.NET MVC 3 のプログラムを作成してみます。

まずはWCF Data Servicesの実装、ということで空のWebアプリケーションプロジェクトを作成します。
WCF Data ServicesでCodeFirstを利用することは、いまのところ予定されている、という状態にとどまっています。
ここではデータベースファーストでWCF Data Servicesを構築することにします。

作成したWebアプリケーションにApp_Dataフォルダを追加し、そこに最初のサンプルで構築されたデータベース(Group.sdfファイル)を追加します。
この時点で一度ビルドを実行しておきます。

Entity Data ModelをGroupModel.edmxという名前で追加します。

image

ウィザードはデフォルトの状態のまますすめていきます。

image

テーブルをモデルに含めます。

image

モデルが作成されます。

image

この時点でまたビルドを実行します。
WCF Data Serviceのページを追加します。

image

データサービスのコードのひな形が作成されるので、以下の赤い部分を追加します。

—————————————————————————————–

using System.Data.Services;
using System.Data.Services.Common;

namespace WebApplication5
{
    public class WcfDataService1 : DataService< GroupEntities >
    {
        // このメソッドは、サービス全体のポリシーを初期化するために、1 度だけ呼び出されます。
        public static void InitializeService(DataServiceConfiguration config)
        {
            config.SetEntitySetAccessRule("*", EntitySetRights.All);
            config.DataServiceBehavior.MaxProtocolVersion = DataServiceProtocolVersion.V2;
        }
    }
}

—————————————————————————————–

プログラムを実行すると、次のような画面が表示され、すでにWebサービスとしてAtomPabの形式でデータの提供が開始されることを確認できます。

image

これでWebサービス側のシステムが構築できたことになります。
このサービスは起動したまま、次の作業に入ります。
サービスが起動していないと以降の作業はうまく動作しませんので気をつけてください。

もうひとつVisual Web Developerを起動し、MVCのプロジェクトを作成します。
後で記述するサンプルコードと名前空間を合わせるため、「MyService」という名前でプロジェクトを作成してください。
ソリューションエクスプローラー上で参照設定を右クリックし、サービス参照の追加を選択します。
アドレス欄にWebサービスのURLをコピーし、名前空間を「GroupServiceReference」としてOKボタンをクリックします。

image

コントローラーとしてMemberController.cs を作成し、以下のコードを記述します。

—————————————————————————————–

using System;
using System.Linq;
using System.Web.Mvc;
using System.Data.Entity.Infrastructure;
using MyService.GroupServiceReference;

namespace MyServiceControllers
{
    public class MemberController : Controller
    {
        private GroupEntities db = new GroupEntities(new Uri("WebサービスのURLをコピー"));

        public ViewResult Index()
        {
            var member = db.Members.Expand("Categories");
            return View(member.ToList());
        }

        public ViewResult Details(int id)
        {
            Members member = db.Members.Where(m => m.ID == id).First();
            return View(member);
        }

        public ActionResult Create()
        {
            ViewBag.CategoryID = new SelectList(db.Categories, "CategoryID", "Name");
            return View();
        }

        [HttpPost]
        public ActionResult Create(Members member)
        {
            if (ModelState.IsValid)
            {
                db.AddToMembers(member);
                db.SaveChanges();
                return RedirectToAction("Index");
            }

            ViewBag.CategoryID = new SelectList(db.Categories, "CategoryID", "Name", member.CategoryID);
            return View(member);
        }

        public ActionResult Edit(int id)
        {
            Members member = db.Members.Where(m => m.ID == id).First();

            ViewBag.CategoryID = new SelectList(db.Categories, "CategoryID", "Name", member.CategoryID);
            return View(member);
        }

        [HttpPost]
        public ActionResult Edit(Members member)
        {
            if (ModelState.IsValid)
            {
                db.AttachTo("Members", member);
                db.UpdateObject(member);
                db.SaveChanges();
                return RedirectToAction("Index");
            }
            ViewBag.CategoryID = new SelectList(db.Categories, "CategoryID", "Name", member.CategoryID);
            return View(member);
        }

        public ActionResult Delete(int id)
        {
            Members member = db.Members.Where(m => m.ID == id).First();
            return View(member);
        }

        [HttpPost, ActionName("Delete")]
        public ActionResult DeleteConfirmed(int id)
        {
            Members member = db.Members.Where(m => m.ID == id).First();
            db.DeleteObject(member);
            db.SaveChanges();
            return RedirectToAction("Index");
        }
    }
}

—————————————————————————————–

このプログラムはCodeFirstのサンプルでスキャッフォールドで生成したコードをもとにしています。
CodeFirstではDbContextを利用していましたが、WCF Data Servicesを参照したこのプログラムではDataServiceContextを利用することになります。
基底となるクラスが用意するメソッドが異なるため、おおきな流れはスキャッフォールドで生成したコードから変えていませんが、利用しているメソッドは異なっている点に注意してください。

次にViewを作成していきます。
まずViewフォルダにMemberフォルダを追加し、その中に下記のコードと追加していきます。

・Create.cshtml

—————————————————————————————–

@model MyService.GroupServiceReference.Members

@{
    ViewBag.Title = "Create";
}

<h2>Create</h2>

<script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>

@using (Html.BeginForm()) {
    @Html.ValidationSummary(true)
    <fieldset>
        <legend>Member</legend>

        <div class="editor-label">
            @Html.LabelFor(model => model.Name)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.Name)
            @Html.ValidationMessageFor(model => model.Name)
        </div>

        <div class="editor-label">
            @Html.LabelFor(model => model.Mail)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.Mail)
            @Html.ValidationMessageFor(model => model.Mail)
        </div>

        <div class="editor-label">
            @Html.LabelFor(model => model.CategoryID, "Category")
        </div>
        <div class="editor-field">
            @Html.DropDownList("CategoryID", String.Empty)
            @Html.ValidationMessageFor(model => model.CategoryID)
        </div>

        <div class="editor-label">
            @Html.LabelFor(model => model.BirthDay)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.BirthDay)
            @Html.ValidationMessageFor(model => model.BirthDay)
        </div>

        <p>
            <input type="submit" value="Create" />
        </p>
    </fieldset>
}

<div>
    @Html.ActionLink("Back to List", "Index")
</div>

—————————————————————————————–

・Delete.cshtml

—————————————————————————————–

@model MyService.GroupServiceReference.Members

@{
    ViewBag.Title = "Delete";
}

<h2>Delete</h2>

<h3>Are you sure you want to delete this?</h3>
<fieldset>
    <legend>Member</legend>

    <div class="display-label">Name</div>
    <div class="display-field">
        @Html.DisplayFor(model => model.Name)
    </div>

    <div class="display-label">Mail</div>
    <div class="display-field">
        @Html.DisplayFor(model => model.Mail)
    </div>

    <div class="display-label">Category</div>
    <div class="display-field">
        @Html.DisplayFor(model => model.Categories.Name)
    </div>

    <div class="display-label">BirthDay</div>
    <div class="display-field">
        @Html.DisplayFor(model => model.BirthDay)
    </div>

</fieldset>
@using (Html.BeginForm()) {
    <p>
        <input type="submit" value="Delete" /> |
        @Html.ActionLink("Back to List", "Index")
    </p>
}

—————————————————————————————–

・Details.cshtml

—————————————————————————————–

@model MyService.GroupServiceReference.Members

@{
    ViewBag.Title = "Details";
}

<h2>Details</h2>

<fieldset>
    <legend>Member</legend>

    <div class="display-label">Name</div>
    <div class="display-field">
        @Html.DisplayFor(model => model.Name)
    </div>

    <div class="display-label">Mail</div>
    <div class="display-field">
        @Html.DisplayFor(model => model.Mail)
    </div>

    <div class="display-label">Category</div>
    <div class="display-field">
        @Html.DisplayFor(model => model.Category.Name)
    </div>

    <div class="display-label">BirthDay</div>
    <div class="display-field">
        @Html.DisplayFor(model => model.BirthDay)
    </div>

    <div class="display-label">Age</div>
    <div class="display-field">
        @Html.DisplayFor(model => model.Age)
    </div>
</fieldset>
<p>
    @Html.ActionLink("Edit", "Edit", new { id=Model.ID }) |
    @Html.ActionLink("Back to List", "Index")
</p>

—————————————————————————————–

・Edit.cshtml

—————————————————————————————–

@model MyService.GroupServiceReference.Members
@{
    ViewBag.Title = "Edit";
}
<h2>
    Edit</h2>
<script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
@using (Html.BeginForm())
{
    @Html.ValidationSummary(true)
    <fieldset>
        <legend>Member</legend>
        @Html.HiddenFor(model => model.ID)
        <div class="editor-label">
            @Html.LabelFor(model => model.Name)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.Name)
            @Html.ValidationMessageFor(model => model.Name)
        </div>
        <div class="editor-label">
            @Html.LabelFor(model => model.Mail)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.Mail)
            @Html.ValidationMessageFor(model => model.Mail)
        </div>
        <div class="editor-label">
            @Html.LabelFor(model => model.CategoryID, "Category")
        </div>
        <div class="editor-field">
            @Html.DropDownList("CategoryID", String.Empty)
            @Html.ValidationMessageFor(model => model.CategoryID)
        </div>
        <div class="editor-label">
            @Html.LabelFor(model => model.BirthDay)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.BirthDay)
            @Html.ValidationMessageFor(model => model.BirthDay)
        </div>
        <p>
            <input type="submit" value="Save" />
        </p>
    </fieldset>
}
<div>
    @Html.ActionLink("Back to List", "Index")
</div>

—————————————————————————————–

・Index.cshtml

—————————————————————————————–

@model IEnumerable<MyService.GroupServiceReference.Members>

@{
    ViewBag.Title = "Index";
}

<h2>Index</h2>

<p>
    @Html.ActionLink("Create New", "Create")
</p>
<table>
    <tr>
        <th>
            Name
        </th>
        <th>
            Mail
        </th>
        <th>
            Category
        </th>
        <th>
            BirthDay
        </th>
        <th></th>
    </tr>

@foreach (var item in Model) {
    <tr>
        <td>
            @Html.DisplayFor(modelItem => item.Name)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.Mail)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.Categories.Name)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.BirthDay)
        </td>
        <td>
            @Html.ActionLink("Edit", "Edit", new { id=item.ID }) |
            @Html.ActionLink("Details", "Details", new { id=item.ID }) |
            @Html.ActionLink("Delete", "Delete", new { id=item.ID })
        </td>
    </tr>
}

</table>

—————————————————————————————–

Viewのコードはスキャッフォールドで作成したコードからmodelの名前空間を変更し、また、計算項目として追加していたAgeがデータベース内にないことからその表示部分を削っただけの修正しかしていません。
これでとりあえず動くサンプルが完成しました。
データの追加/削除/変更が自由に行え、それがWebサービスをとおしてデータベース内に反映されることを確認してみましょう。

少しさわっていると、バリデーションが行われないことに気づくと思います。
最初のサンプルと同様のバリデーションを組み込むにはMetaDataを利用するのが簡単です。
Modelsフォルダに以下のコードを追加します。

・MetaClass.cs

—————————————————————————————–

using System.ComponentModel.DataAnnotations;

namespace MyService.GroupServiceReference
{
    [MetadataType(typeof(MyMetaData))]
    public partial class Members
    {
    }

    public class MyMetaData
    {
        [Required(ErrorMessage="名前は必須入力です")]
        public object Name { get; set; }
        [RegularExpression(@"[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4}", ErrorMessage = "メールが正しくありません")]
        public object Mail { get; set; }

    }
}

—————————————————————————————–

これでバリデーションが実行されるようになります。
MetaDataを利用する際に一番注意が必要なことは上記のプログラムでいうとpartialクラスとして定義しているMembersの名前空間を含めた定義になります。
この部分がバリデーションをあてようとしている元のクラスと正しく一致しないと設定が反映されません。
やっかいなことに元のクラスと一致していない場合でもコンパイルエラーにはならないため、なぜ設定が反映されないのか悩んで悩んで、結局このクラス定義にミスがあった、ということがよくあります。

 

○ふりかえりのまとめ

 

ASP.NET MVC では、.NET Frameworkが用意しているデータ連携の機能すべてを利用できます。
CodeFirstベースでデータベースを利用することもできますし、このサンプルのようにODataベースのWebサービスを簡単に利用することもできます。
今回は説明していませんが、XMLベースでのデータ配信を取り込んで利用するのも簡単です。
そして、多少利用できるメソッドの違いはありますが、全体的な流れはModel側のデータがなんであっても変わりません。

ASP.NET MVC 3ではスキャッフォールドで生成されるコードがかなり参考にできるコードになったので、ぜひ一度自分で試して、中身を納得できるまでよく解析してみると良いと思います。

なおセッション中では特に言及しませんでしたが、このBlogの内容はすべて無償版であるVisual Web Developerで動作させ、確認を行っています。
WebPIからVisual Web Developerをインストールすると必要な環境はすべて整いますので、ぜひインストールして試してみてください。

ASP.NET MVCはWebアプリケーションの開発において非常に魅力的な技術です。
CodeFirstの登場により、開発者にとってはその魅力がより増した、と感じています。
これらの技術に触れる最初の手がかりとして、このBlog記事が役に立つといいな、と思っています。