ホーム > ASP.NET MVC 3, mvcConf @:Japan, WCF Data Services > mvcConf @:Japan ふりかえり 6: Webサービス(OData)連携

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

.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記事が役に立つといいな、と思っています。

  1. まだコメントはありません。
  1. 2011年6月22日 5:47 PM

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト / 変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト / 変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト / 変更 )

Google+ フォト

Google+ アカウントを使ってコメントしています。 ログアウト / 変更 )

%s と連携中

%d人のブロガーが「いいね」をつけました。