일반적으로는 Controller의 Action Method에서 View로 Model을 전달할때는 한가지 형식의 모델만 전달하는 것이 보통이며 아래는 그에 해당하는 예제입니다.
public class Member
{
public string Id { get; set; }
public string Name { get; set; }
public int Level { get; set; }
}
public class Members
{
private static Members mbs = new Members();
public static Members GetMbs
{ get { return mbs; }
}
public List<Member> MemberList = new List<Member> {
new Member { Id = "cliel1", Name = "홍길동", Level = 10 },
new Member { Id = "cliel2", Name = "홍길순", Level = 20 },
new Member { Id = "cliel3", Name = "홍길남", Level = 30 },
new Member { Id = "cliel4", Name = "홍길영", Level = 40 },
new Member { Id = "cliel5", Name = "홍길석", Level = 50 }
};
public IEnumerable<Member> GetMember()
{
return MemberList;
}
public bool AddMember(Member m)
{
MemberList.Add(m);
return true;
}
}
Member 라는 클래스를 통해 Model을 정의했으며 Members로 이 Model에 대한 리스트를 가져오거나 추가하는 클래스를 작성하였습니다.
private Members mbs = Members.GetMbs;
// GET: Home
public ActionResult Index()
{
return View((mbs.GetMember()));
}
Home 컨트롤러에서는 Members의 정적 멤버를 이용해 IEnumerable 형식으로 Member 리스트를 가져오도록 하였습니다.
@using WebApplication1.Models
@model IEnumerable<WebApplication1.Models.Member>
@{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Index</title>
</head>
<body>
<table>
@foreach (Member m in Model) {
<tr>
<td>@m.Id</td>
<td>@m.Name</td>
<td>@m.Level</td>
</tr>
}
</table>
</body>
</html>
뷰에서는 전달된 모델을 통해 요소를 순회하면서 각 Member의 리스트를 표시하고 있습니다.
이제 이 상태에서 새로운 Member 개체를 추가해야 한다고 가정해 보겠습니다. 우리는 이 기능의 구현을 위해 입력컨트롤 생성을 위한 TextBoxFor 와 같은 메서드를 사용하려고 합니다. 그런데 현재 Model수준에서는 이 메서드사용이 불가능합니다. 전달된 Model형식자체가 배열형식이기 때문입니다. 이 문제점을 해결하기 위해 여기서는 Tuple 클래스를 사용해 보려고 합니다.
public ActionResult Index()
{
var t = new Tuple<IEnumerable<Member>, Member>(mbs.GetMember(), new Member()) { };
return View(t);
}
먼저 Controller를 위와 같이 수정합니다. 보시면 Tuple를 통해 IEnumerable형식의 Member와 단일 형식의 Member를 감싸고 있습니다. <>안에서는 전달하고자 하는 Model의 형식을 넣고 ()안에는 실제 전달된 Model자체를 지정하여 2가지 형식의 Model을 묶는 것입니다. 그리고 이렇게 생성된 인스턴스를 View메서드를 통해 전달하고 있습니다.
@model Tuple<IEnumerable<WebApplication1.Models.Member>, WebApplication1.Models.Member>
전달될 Model을 위와같이 변경하였으니 뷰에서도 그에 맞추어 model 형식선언을 변경해야 합니다. 다만 이때는 Tuple에서 <>안에 Model의 형식만을 지정합니다.
그렇다면 어떻게 Tuple안에 있는 형식에 접근할 수 있을까? Tuple는 이를 위해 Item이라는 속성을 제공하고 있습니다.
<table>
@foreach (Member m in Model.Item1) {
<tr>
<td>@m.Id</td>
<td>@m.Name</td>
<td>@m.Level</td>
</tr>
}
</table>
기존의 foreach가 사용된 부분을 주목해 주세요. Model에서 Model.Item1로 Model지정방식이 변경되었습니다. Tuple에서 첫번째 Model형식이 IEnumerable이고 foreach안에서는 해당 형식을 다루어야 하므로 Item1속성을 통해 IEnumerable이 지정된 Model에 접근하고자 하는 것입니다.
@using (Html.BeginForm("AddMember", "Home")) {
<p>ID : </p>@Html.TextBoxFor(x => x.Item2.Id)<br />
<p>NAME : </p>@Html.TextBoxFor(x => x.Item2.Name)<br />
<p>LEVEL : </p>@Html.TextBoxFor(x => x.Item2.Level)<br />
<input type="submit" value="추가" />
}
이제 위에서 처럼 Member를 추가할 Form을 만듭니다. 이때 Member의 각 속성은 Model의 Item2 속성을 통해 접근하고 있는데 이는 Tuple에서 두번째 Model이 단일 Member이기 때문입니다.
public ActionResult AddMember([Bind(Prefix ="Item2")]Member m)
{
mbs.AddMember(m);
return RedirectToAction("Index");
}
Member 추가에 사용될 액션메서드입니다. 특히 메서드의 매개변수에서 Bind의 Prefix속성을 통해 Item2라고 지정하고 있는데 만약 이렇게 하지 않으면 item2.id나 item2.Name과 같은 속성이름을 통해 Model을 바인딩하려고 시도할 것입니다. 하지만 이 속성값들은 실제 Member의 속성과 일치하지 않기 때문에 바인딩에 실패하게 되고 따라서 속성을 정확히 일치시키기 위해 Item2를 Prefix로 지정하여 Item2안에서의 속성만으로 Member의 Model에 바인딩될 수 있도록 해야합니다.