잊지 않겠습니다.

MVC Model은 매우 멋진 Web Model이고, View에서 표현하는 방법이 표준 Html을 기본적으로 사용하고 있기 때문에 매우 만족스러울 뿐 아니라, 디자이너와의 협업에서 뛰어난 적응도를 보여준다.

기존의 ASP .NET에서는 수 많은 서버 control들이 있었고, 이러한 서버 control들은 각각의 사용법 및 세부적인 디자인을 변경시키는 데에 있어서 많은 어려움을 가지고 있었던 것이 사실이다. (내가 잘 못하는 것도 있겠지만. –-;;)

MVC 모델을 사용하다가 자주 만드는 것 중 하나가 table인데, 각각의 데이터에 따라서 매번 다른 table을 반복작업을 하는 것이 싫어서 하나의 MVC user control을 만들었다.

먼저 LINQ를 이용한 데이터의 pagination을 제공하고, table에 각각 필요한 데이터를 Model로 선언한다. 그리고 table의 pagination은 AJAX call을 기반으로 한다. 

LINQ를 이용한 PagedList : 

   public interface IPagedList
    {
        int TotalCount { get; set; }
        int PageIndex { get; set; }
        int PageSize { get; set; }

        bool IsPreviousPage { get;  }
        bool IsNextPage { get;  }

        int TotalPageCount { get; set; }
    }

    public class PagedList<T> : List <T>, IPagedList
    {
        public PagedList(IQueryable<T> source, int index, int pageSize)
        {
            TotalCount = source.Count();
            PageSize = pageSize;
            PageIndex = index;
            TotalPageCount = TotalCount/PageSize;
            AddRange(source.Skip(index*pageSize).Take(PageSize).ToList());
        }

        public PagedList(List<T> source, int index, int pageSize)
        {
            TotalCount = source.Count();
            PageSize = pageSize;
            PageIndex = index;
            TotalPageCount = TotalCount / PageSize;
            AddRange(source.Skip(index * pageSize).Take(PageSize).ToList());
        }

        #region IPagedList Members

        public int TotalCount
        {
            get;
            set;
        }

        public int PageIndex
        {
            get;
            set;
        }

        public int PageSize
        {
            get;
            set;
        }

        public bool IsPreviousPage
        {
            get
            {
                return PageIndex > 0;
            }
        }

        public bool IsNextPage
        {
            get
            {
                return ( (PageIndex + 1) * PageSize ) < TotalCount;
            }
        }

        public int TotalPageCount { get; set; }

        #endregion
    }

    public static class Pagiation
    {
        public static PagedList<T> ToPagedList<T>(this IQueryable<T> source, int index, int pageSize)
        {
            return new PagedList<T>(source, index, pageSize);
        }

        public static PagedList<T> ToPagedList<T>(this List<T> source, int index, int pageSize)
        {
            return new PagedList<T>(source, index, pageSize);
        }
    }

PagedTableData : Table에 필요한 데이터들
    public class PagedTableData
    {
        public List<string> ColumnHeaders { get; set; }     //Column Headers
        public string TableId { get; set; }                 //Html Id
        public PagedList<string[]> PagedList { get; set; }  //Page<tbody> datas
        public string ActionName { get; set; }              //Ajax call Action data
    }

PagedTable : MVC User Control 
PagedTable.ascx >>
<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="PagedTable.ascx.cs" Inherits="SYNCmailAdmin.Views.Shared.PagedTable" %>
<div id="<%=ViewData.Model.TableId %>">
  <table class="mainTable" width="100%">
    <tr>
      <%foreach(string column in ViewData.Model.ColumnHeaders) { %>
      <th><%=column %></th>
      <%} %>
    </tr>
    
    <%foreach(string[] datas in ViewData.Model.PagedList) { %>
      <tr>
        <%foreach(string data in datas) { %>
          <td><%=data%></td>
        <%} %>
      </tr>
    <%} %>  
  </table>
  <div align="center">
    <%
      int pageIndex = ViewData.Model.PagedList.PageIndex;
      int startIndex = Math.Max(0, pageIndex - 5);
      int endIndex = Math.Min(pageIndex + 5, ViewData.Model.PagedList.TotalPageCount);
    %>
    <%if(ViewData.Model.PagedList.IsPreviousPage) { %>    
      <%for(int i = startIndex ; i < pageIndex ; i++){ %>
      <%=Ajax.ActionLink(i.ToString(), ViewData.Model.ActionName, new {pageIndex = i}, new AjaxOptions() { UpdateTargetId = ViewData.Model.TableId}) %>
      <%} %>
    <%} %>
    <strong><%=pageIndex%></strong>
    <%if(ViewData.Model.PagedList.IsNextPage) { %>
      <%for(int i = pageIndex + 1 ; i <= endIndex ; i++) { %>
      <%=Ajax.ActionLink(i.ToString(), ViewData.Model.ActionName, new {pageIndex = i}, new AjaxOptions() {UpdateTargetId = ViewData.Model.TableId}) %>
      <%} %>
    <%} %>    
  </div>
</div>


PagedTable.ascx.cs
    public partial class PagedTable : System.Web.Mvc.ViewUserControl<PagedTableData>
    {
    }


사용법은 다음과 같다. 
        public ActionResult Index()
        {
            ViewData["Title"] = "Home Page";
            ViewData["Message"] = "Welcome to ASP.NET MVC!";

            HomeIndexDataModel dataModel = new HomeIndexDataModel() {PagedTableData = GetPagedData(null)};
            return View(dataModel);
        }

        public ActionResult GetPagedDataModel(int? pageIndex)
        {
            return View("PagedTable", GetPagedData(pageIndex));
        }

        private PagedTableData GetPagedData(int? pageIndex)
        {
            PagedTableData pageData = new PagedTableData();
            pageData.ColumnHeaders = new List<string>() {"FirstData", "SecondData"};
            pageData.TableId = "TableId";
            pageData.PagedList = TableTempDataGenerator.GetDatas()
                .ToPagedList(pageIndex == null ? 0 : pageIndex.Value, 10);
            pageData.ActionName = "GetPagedDataModel";

            return pageData;
        }

각 Column Header와 Table의 ActionResult 값을 얻어오는 함수명을 넣어주는 것으로 쉽게 해결 가능하다. 조금 제약사항이 되는 것이, GetPagedData에서 반드시 pageIndex라는 변수명으로 적어줘야지 되는 제약사항이 걸리게 된다. 

실행 결과는 다음과 같다.

Sample Source도 같이 올립니다. 필요하신 분들은 얼마든지 퍼가세요.
(이곳에 오시는 분들이 있으시다면.. ㅋㅋ ^^)
Posted by Y2K
,
1. XDocument to XmlDocument
            XDocument xDocument = new XDocument();
            XmlDocument xmlDoc = new XmlDocument();

            xmlDoc.Load(xDocument.CreateReader());

2. XmlDocument to XDocument
            StringReader sr = new StringReader(xmlDoc.InnerXml);
            XDocument xDocument = XDocument.Load(XmlReader.Create(sr));


Posted by Y2K
,

Pro Linq (06)

Book 2009. 1. 13. 10:51
LINQ를 이용한 XML의 검색방법 역시 기존의 LINQ와 동일하다. 
특징적으로 이야기할 수 있는 사항은 각각의 XML의 element, attribute, comment 등으로 검색이 가능하다는 점으로, DB LINQ나 Object LINQ에서 사용하는 것과 같이 XML의 특성으로 검색이 가능하다. 

조금 복잡한 LINQ query를 하나 예시로 든다면.. 

            var biddata = from b in bids.Descendants("bid_tuple")
                          where double.Parse(b.Element("bid").Value) > 50
                          join u in users.Descendants("user_tuple")
                              on b.Element("userid").Value equals u.Element("userid").Value
                          join i in items.Descendants("item_tuple")
                              on b.Element("itemno").Value equals i.Element("itemno").Value
                          select new
                                     {
                                         Item = b.Element("itemno").Value,
                                         Description = i.Element("description").Value,
                                         User = u.Element("name").Value,
                                         Date = b.Element("bid_date").Value,
                                         Price = b.Element("bid").Value
                                     };
            foreach(var bd in biddata)
            {
                Console.WriteLine("{0, -12} {1, -12} {2, -6}, {3, -14}, {4:C, 10}", bd.Date, bd.User, bd.Item,
                                  bd.Description, bd.Price);
            }

보면 재미있는 내용중 하나가 select 구문에서의 새로운 Class의 생성이다. 이 Class는 따로 선언되어있지 않으며, 각각의 Property의 이름을 각각 적어서 생성하게 된다. 이 부분은 C# 4.0에서 dynamic programming과 비슷하게 보이지만 dynamic으로 선언된것과는 다르게 사용된다. var로 선언되어 익명 class로 생성된 상태이다. 

여기에서 bd에 대해 type을 얻어보면 다음과 같다.
<>f__AnonymousType1`5[String, String, String, String, String]
사용된 Item, Description, User, Date, Price에 대해서 5개의 Property를 갖는 익명 Class의 선언으로 되어 있는 것을 알 수 있다.
Posted by Y2K
,