アーカイブ

Archive for 2011年6月21日

WEB+DB PRESS に .NETの記事を書きました(そして続きます)

2011年6月21日 コメントを残す

WEB+DB PRESS Vol.63

こちらの雑誌に「いまどきの.NET開発」という題で1年間(隔月刊なので6回)連載することになりました。
Vol.63には第一回 「.NETの現在(いま)」という記事が載っています。

「.NET開発」という言葉の指す範囲がだいぶ広くなってますので、そのあたりの誤解とか勘違いとか最近私が気になってたところをまとめた記事です。
このため、開発系の雑誌なのに、プログラムコードが1行も書いてないという。。。(w

まぁ、次からは少しコードもでてくるかと。

1年間という長丁場なので、先のことはまだ確定していなかったりします。
リクエストがあれば、それにお答えした記事を書くことも可能かもしれませんので、もし何かありましたらご連絡いただけるとうれしいです。

そのまえに、まず読んでいただけるとほんとありがたく。

カテゴリー:雑記

mvcConf @:Japan ふりかえり 5: 同時実行制御

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

スライド15

CodeFirstとASP.NET MVCとの組み合わせでは同時実行制御も簡単に実装することができます。
ふりかえり1で作成したサンプルプログラムを同時実行制御のエラーチェックができるように修正してみましょう。

まず、POCOのクラスに以下のようにTimestamp属性を付加した制御用の項目を追加します

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

public class Member
{
    public int ID { get; set; }
    [Required(ErrorMessage="名前は必須入力です")]
    public string Name { get; set; }
    [RegularExpression(@"[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4}", ErrorMessage = "メールが正しくありません")]
    public string Mail { get; set; }
    public int CategoryID { get; set; }
    public DateTime? BirthDay { get; set; }
    public int? Age
    {
        get
        {
            int? age = null;
            if (BirthDay != null)
            {
                age = DateTime.Now.Year – BirthDay.Value.Year;
                if (DateTime.Now < BirthDay.Value.AddDays(-1).AddYears(age.Value)) age -= 1;
            }
            return age;
        }
    }
    [Timestamp]
    public Byte[] Timestamp { get; set; }

    public virtual Category Category { get; set; }

}

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

これにより、データベースにTimestamp項目が追加されます。
データの型がByte[]型であることに注意しておきましょう。

スキャッフォールドで生成されるコントローラー、ビューはTimestamp項目がないときと同じです。
そのままだと更新時にエラーとなるので、Editビュー(Edit.cshtml)にhiddenとしてTimestamp項目を追加します。

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

@model MyDal.Member

@{
    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>
        @Html.HiddenFor(Model => Model.Timestamp)
        <p>
            <input type="submit" value="Save" />
        </p>
    </fieldset>
}

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

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

同時実行制御のエラーが発生したときは例外としてDbUpdateConcurrencyExceptionがthrowされるので、このエラーをキャッチします。
実際のエラー時の対処はシステムの仕様によって異なります。
ここではとりあえずエラー時のメッセージを表示するようにだけEditアクションに手を入れます。

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

[HttpPost]
public ActionResult Edit(Member member)
{
   try
    {
        if (ModelState.IsValid)
        {
            db.Entry(member).State = EntityState.Modified;
            db.SaveChanges();
            return RedirectToAction("Index");
        }
    }
    catch (DbUpdateConcurrencyException e)
    {
        ModelState.AddModelError(string.Empty, e.Message);
    }
    ViewBag.CategoryID = new SelectList(db.Categories, "CategoryID", "Name", member.CategoryID);
    return View(member);
}

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

このようにサンプルプログラムを修正して実行します。
ブラウザを2つ起動して、片方でEdit画面を表示させた状態でもう1つのブラウザから同じデータを更新します。
その後、先にEdit画面を表示していたほうでデータを更新しようとすると以下のようなエラーが表示されます。

image

このサンプルで基本的な動作は理解していただけると思います。
より実際の動きに近い形の同時実行制御のサンプルがこちらで公開されています。
ぜひ参考にしてみてください。