読者です 読者をやめる 読者になる 読者になる

1.21 jigowatts

Great Scott!

ASP.NET MVCはじめました~データの新規登録

ASP.NET MVC

概要

一覧表示詳細表示に引き続き、今回はデータの新規登録です。

環境

Visual Studio 2010
ASP.NET MVC2
SQLServer2008 R2

今回の要件は

  1. ブラウザより値を入力
  2. データベースに入力値を登録

として、サンプルコードを書いてみました。

実装

コントローラーの用意

入力の検証のために、属性を指定したクラスを受け取るコントローラーメソッドを用意します。

using System;
using System.Web.Mvc;
using Mvc2App.Models;
using Mvc2App.Services;
using Mvc2App.Services.Abstractions;
using Mvc2App.ViewModels.Peple;
using System.Data;
using Mvc2App.Common;

namespace Mvc2App.Controllers
{
    [Authorize]
    public class PepleController : Controller
    {
        IPepleService _service;

        public PepleController() : this(new PepleService())
        { 
            
        }

        public PepleController(IPepleService service)
        {
            _service = service;
        }

        ...

        public ActionResult Create()
        {
            return View();
        }

        [HttpPost]
        public ActionResult Create(PepleViewModel inputModel)
        {
            try
            {
                var person = new Person()
                {
                    Name = inputModel.Name,
                    Address = inputModel.Address,
                    PhoneNumber = inputModel.PhoneNumber,
                    UpdatedBy = Session["LogOnUser"].ToString(),
                    UpdateDate = DateTime.Now
                };

                _service.Insert(person);
                return RedirectToAction("Index");
            }
            catch(ConstraintException)
            {
                inputModel.SysMessage = Resource.GetValue("ERR_002");
                return View(inputModel);
            }
            catch (UpdateException)
            {
                inputModel.SysMessage = Resource.GetValue("ERR_002");
                return View(inputModel);
            }
        }
    }
}
サービスインタフェースとサービスクラスの用意
using System;
using System.Collections.Generic;
using System.Web.Mvc;
using Mvc2App.Models;
using Mvc2App.ViewModels.Peple;

namespace Mvc2App.Services.Abstractions
{
    public interface IPepleService
    {
        ...
        void Insert(Person p);
    }
}
using System;
using System.Collections.Generic;
using System.Web.Mvc;
using Mvc2App.Models;
using Mvc2App.Repositories;
using Mvc2App.Repositories.Abstractions;
using Mvc2App.Services.Abstractions;
using Mvc2App.ViewModels.Peple;
using Mvc2App.Common;

namespace Mvc2App.Services
{  
    public class PepleService : IPepleService
    {
        private IPepleRepository _repository;
        public PepleService() : this(new PepleRepository())
        { 
        }

        public PepleService(IPepleRepository repository)
        {
            _repository = repository;
        }

        ...

        public void Insert(Person p) 
        {
            _repository.Insert(p);
        }
    }
}
リポジトリインタフェースとリポジトリクラスの用意
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Mvc2App.Models;

namespace Mvc2App.Repositories.Abstractions
{
    public interface IPepleRepository
    {
        ...
        void Insert(Person p);
    }
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Mvc2App.Repositories.Abstractions;
using Mvc2App.Models;

namespace Mvc2App.Repositories
{
    public class PepleRepository : IPepleRepository
    {
        DevEntities dbContext = new DevEntities();

        ...

        public void Insert(Person p) 
        {
            dbContext.Peple.AddObject(p);
            dbContext.SaveChanges();
        }
    }
}
ビューモデルの用意

入力の検証を行うためにビューモデル*1にデータアノテーション属性を指定します。また、共通のシステムメッセージを保持するためにViewModelBaseクラスを継承するようにPepleViewModelクラスを変更しました。

namespace Mvc2App.ViewModels
{
    public class ViewModelBase
    {
        public string SysMessage { get; set; }
    }
}
using System;
using System.ComponentModel.DataAnnotations;
using Mvc2App.Content;

namespace Mvc2App.ViewModels.Peple
{
    public class PepleViewModel : ViewModelBase
    {
        public int ID { get; set; }

        [Required(ErrorMessageResourceName = "VALID_001", ErrorMessageResourceType = typeof(Strings))]
        [StringLength(20, ErrorMessageResourceName = "VALID_002", ErrorMessageResourceType = typeof(Strings))]
        public string Name { get; set; }

        [StringLength(50, ErrorMessageResourceName = "VALID_002", ErrorMessageResourceType = typeof(Strings))]
        public string Address { get; set; }

        [StringLength(15, ErrorMessageResourceName = "VALID_002", ErrorMessageResourceType = typeof(Strings))]
        public string PhoneNumber { get; set; }

        public string UpdatedBy { get; set; }
        public DateTime UpdateDate { get; set; } 
    }
}

テーブル定義もNameとUpdateDateをNot Nullに修正しました。

CREATE TABLE [dbo].[Peple](
	[ID] [int] IDENTITY(1,1) NOT NULL,
	[Name] [nvarchar](20) NOT NULL,
	[Address] [nvarchar](50) NULL,
	[PhoneNumber] [nvarchar](15) NULL,
	[UpdatedBy] [nvarchar](20) NULL,
	[UpdateDate] [datetime] NOT NULL,
 CONSTRAINT [PK_Peple] PRIMARY KEY CLUSTERED 
(
	[ID] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]

これに伴い、NameとUpdateDateのエンティティもNull許容プロパティをFalseへ変更。
f:id:sh_yoshida:20141111102535p:plain

ビューの用意
<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" 
Inherits="System.Web.Mvc.ViewPage<Mvc2App.ViewModels.Peple.PepleViewModel>" %>

<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
	Create
</asp:Content>

<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">

    <h2>Create</h2>
    <div id="sysMessage"><%: Html.DisplayFor(model => model.SysMessage) %></div>
    <% using (Html.BeginForm()) {%>
        <%: Html.ValidationSummary(true) %>

        <fieldset>
            <legend>Fields</legend>
            <div>
                <label>Name</label>
            </div>
            <div>
                <%: Html.TextBoxFor(model => model.Name) %>
                <%: Html.ValidationMessageFor(model => model.Name) %>
            </div>
            <div>
                <label>Address</label>
            </div>
            <div>
                <%: Html.TextBoxFor(model => model.Address) %>
                <%: Html.ValidationMessageFor(model => model.Address)%>
            </div>
            <div>
                <label>PhoneNumber</label>
            </div>
            <div>
                <%: Html.TextBoxFor(model => model.PhoneNumber) %>
                <%: Html.ValidationMessageFor(model => model.PhoneNumber)%>
            </div>
            <p>
                <input type="submit" value="Create" />
            </p>
        </fieldset>
    <% } %>
    <div>
        <%: Html.ActionLink("Back to List", "Index") %>
    </div>
</asp:Content>

システムメッセージは赤で表示させるようにCSSも追記。

#sysMessage
{
    color: #ff0000;    
}
リソースの用意

メッセージはリソースファイルより取得するように変更します。
プロジェクトにリソースファイル(ファイル名はStrings.resx)を追加し、メッセージを整備します。
f:id:sh_yoshida:20141111102556p:plain
ユーティリティクラスも用意し、こちらからメッセージを取得するようにしました。

using System;

namespace Mvc2App.Common
{
    public class Resource
    {
        public static string GetValue(string name)
        {
            return Strings.ResourceManager.GetString(name);
        }
    }
}
ログオンユーザのセッション管理

データベースにデータを登録する際の登録者はセッションより取得しています。
セッションをセットするタイミングはGlobal.asaxファイルのSession_Startメソッドに記述しました。

using System.Web.Mvc;
using System.Web.Routing;

namespace Mvc2App
{
    public class MvcApplication : System.Web.HttpApplication
    {

        ...

        protected void Session_Start()
        {
            if (Session["LogOnUser"] == null) 
            {
                if (User.Identity.IsAuthenticated) 
                {
                    Session["LogOnUser"] = HttpContext.Current.User.Identity.Name.Split('\\')[1];
                }
            }
        }

    }
}

User.Identity.Nameよりユーザ名を取得しています。
デフォルトのWeb.configではフォーム認証になっているのでUser.Identity.Nameが取得できません。
Windows認証に変更してあげると、User.Identity.Nameが取得できるようになります。
HttpContext.User プロパティ (System.Web)

<authentication mode="Windows"></authentication>

実行結果

/Peple/Createにアクセスします。


f:id:sh_yoshida:20141111110500p:plain
正しい値を入力しCreateボタンを押下すると正常にデータが登録され、一覧画面へ遷移します。


f:id:sh_yoshida:20141111110510p:plain
UpdatedBy(更新者)カラムはセッションの値より、UpdateDate(更新日)は実行日時で登録されます。


f:id:sh_yoshida:20141111110524p:plain
不正な値を入力した場合は、入力の検証にて無効な値と判断されエラーメッセージが表示されます。


ASP.NET MVCはじめました~データベースより値を取得し一覧表示する - 1.21 jigowatts

ASP.NET MVCはじめました~データベースより値を取得し詳細を表示する - 1.21 jigowatts

*1:入力モデル用のクラスを用意したほうがよさそう?