WebForms 개발시 Repeater를 자주 사용하게 된다. (회사마다 다르겠지만)
Repeater 사용시 중첩으로 사용하고 싶은 경우들이 있다.

예를들어.

A사 (Key: A)  
  A-1 계열사 (FKey: A)
B사 (Key: B)  
  B-1 계열사 (FKey: B)
  B-2 계열사 (FKey: B)

위와 같은 테이블을 만들기 위해 중첩 Repeater를 사용할 수 있다. (물론 DB조회를 잘 해와서 하나의 Repeater로도 충분히 표현 가능하다. 예시일뿐.)

사용하려는 방법은 DataSet의 Relations를 활용한 방법이다.
* MS Docs DataRelation 참고
Relations를 활용하면 키값으로 두개의 DataTable을 묶어서 사용할 수 있다.
구글링을 하여 찾아보면, OnItemDataBound에서 한번 더 쿼리를 실행해 바인딩(부모의 매 Row마다 실행)하는 방법을 많이 볼 수 있다.
하지만 DataRelation을 사용하면 한번의 쿼리 실행으로 원하는 결과를 만들 수 있다.

코드를 확인하며 이해해보자

[aspx]
Repeater 두개를 중첩하여 작성하였다.
OnItemDataBound에서 Relation정보를 찾아와 SubCompanyList에 바인딩 한다.
    * MS Docs DataBinder.Eval 참고

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<table>
    <tbody>
        <asp:Repeater ID="CompanyList" runat="server" OnItemDataBound="CompanyList_ItemDataBound">
            <ItemTemplate>
                <tr>
                    <td><%# Eval("Name") %> (Key: <%# Eval("Key") %>)</td>
                    <td>&nbsp;</td>
                </tr>
                <asp:Repeater ID="SubCompanyList" runat="server">
                    <ItemTemplate>
                        <tr>
                            <td>&nbsp;</td>
                            <td><%# DataBinder.Eval(Container.DataItem, "[\"Name\"]") %> (FKey: <%# DataBinder.Eval(Container.DataItem, "[\"FKey\"]") %>)</td>
                        </tr>
                    </ItemTemplate>
                </asp:Repeater>
            </ItemTemplate>
        </asp:Repeater>
    </tbody>
</table>
cs

 

[aspx.cs]
1. Page_Load에서 dtCompany, dtSubCompany(임시로 만든 정보입니다. 현업에서는 DB를 읽어와 사용하겠죠?)를 불러와 DataSet을 생성한다.
2. DataSet.Relations를 추가 하여 관계를 생성한다. (관계의 이름은 "CompanyKey"이다.)
    * Relations의 파라미터로 "관계의 이름", "부모 키 컬럼", "자식 키 컬럼"을 넣어준다.

3. 최상단 Repeater에 부모 Table을 바인딩한다.
4. OnItemDataBound Event에서 자식 Table을 찾아 Sub Repeater에 바인딩한다. (GetChildRows 함수 사용)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
 
protected void Page_Load(object sender, EventArgs e)
{
    var dtCompany = InitCompanyData();
    var dtSubCompany = InitSubCompanyData();
    // 데이터셋 생성
    var ds = new DataSet();
    ds.Tables.Add(dtCompany);
    ds.Tables.Add(dtSubCompany);
    // 관계 설정
// DB 조회시에는 SP에서 테이블을 여러개 읽어와 Fill로 DataSet을 만든 후 Relation만 잡아주면 된다.
    ds.Relations.Add("CompanyKey", ds.Tables["CompanyTable"].Columns["Key"], ds.Tables["SubCompanyTable"].Columns["FKey"]);
    // 부모 테이블 바인딩
    CompanyList.DataSource = ds.Tables["CompanyTable"];
    CompanyList.DataBind();
}
 
private DataTable InitCompanyData()
{
    var dtCompany = new DataTable();
    dtCompany.TableName = "CompanyTable";
    dtCompany.Columns.Add("Key"typeof(string));
    dtCompany.Columns.Add("Name"typeof(string));
    var newRow = dtCompany.NewRow();
    newRow["Key"= "A";
    newRow["Name"= "A사";
    dtCompany.Rows.Add(newRow);
    newRow = dtCompany.NewRow();
    newRow["Key"= "B";
    newRow["Name"= "B사";
    dtCompany.Rows.Add(newRow);
    return dtCompany;
}
 
private DataTable InitSubCompanyData()
{
    var dtSubCompany = new DataTable();
    dtSubCompany.TableName = "SubCompanyTable";
    dtSubCompany.Columns.Add("FKey"typeof(string));
    dtSubCompany.Columns.Add("Name"typeof(string));
    var newRow = dtSubCompany.NewRow();
    newRow["FKey"= "A";
    newRow["Name"= "A-1 계열사";
    dtSubCompany.Rows.Add(newRow);
    newRow = dtSubCompany.NewRow();
    newRow["FKey"= "B";
    newRow["Name"= "B-1 계열사";
    dtSubCompany.Rows.Add(newRow);
    newRow = dtSubCompany.NewRow();
    newRow["FKey"= "B";
    newRow["Name"= "B-2 계열사";
    dtSubCompany.Rows.Add(newRow);
    return dtSubCompany;
}
 
protected void CompanyList_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
    if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem)
    {
        Repeater SubCompanyList = e.Item.FindControl("SubCompanyList"as Repeater;
        // 관계가 설정된 자식 테이블 얻어오기
// 이 부분에서 한번 더 쿼리를 실행할 필요 없이, 관계가 맺어진 정보를 읽어올 수 있다.
        var childRows = ((DataRowView)e.Item.DataItem).Row.GetChildRows("CompanyKey");
        SubCompanyList.DataSource = childRows;
        SubCompanyList.DataBind();
    }
}
cs

 

 

[결과]

직접 디버깅을 해보면 알겠지만 OnItemDataBound Event에서 부모 Table의 Row에 자식 테이블이 엮여 있는것을 볼 수 있다.

 

* DB조회시 까다로운 쿼리 혹은 전혀 관계 없는 데이터를 코드딴에서 묶어서 사용하고 싶을때도 유용하게 사용가능하다. 일자별로 묶어서 데이터를 처리한다던가 하는 그런것들 말이다.

 

다음번 글은 MS사의 Bot Framework와 Language Understanding(LUIS:루이스)을 작성해볼까 한다. (텔레그램, 스카이프 등등 연동도 간편하고 좋다. 단지 LUIS가 유료)

 

끗.

WebForm 개발시 UpdatePanel을 자주 사용하게 된다.


클라이언트(브라우저)딴에서 UpdatePanel을 이용하여 동적으로 화면을 그릴 경우 Load되는 타이밍을 잡아 이벤트바인딩, 데이터바인딩 등의 작업을 해야할 경우가 있다.

이때 Sys.Application을 사용하여 이벤트를 잡을 수 있다.


Sys.Application란, ASP.NET WebForm에서 클라이언트딴의 이벤트를 제어할 수 있도록 제공하는 클라이언트딴 클래스이다.

해당 클래스를 이용하여 Load 이벤트를 잡아보자.


* Sys.Application 클래스에는 Load 뿐 아니라 다양한 이벤트들이 존재합니다. 문서 확인 후 응용하시면 됩니다.


사용 예

[클라이언트]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<html>
<head>
    <script>
        function UpdatePanel_LoadEvent() {
            console.log($('#contents').text());
        }
    </script>
</head>
<body>
    <asp:ScriptManager ID="ScriptManager1" runat="server"></asp:ScriptManager>
    <asp:UpdatePanel ID="UpdatePanel1" runat="server">
        <ContentTemplate>
            <script>
                // Sys.Application 클래스를 이용하여 이벤트 등록
                Sys.Application.add_load(UpdatePanel_LoadEvent);
            </script>
            <asp:Label ID="contents" ClientIDMode="Static" runat="server" Text="내용입니다."></asp:Label>
        </ContentTemplate>
        <Triggers>
            <asp:AsyncPostBackTrigger ControlID="Button_Submit" EventName="Click" />
        </Triggers>
    </asp:UpdatePanel>
 
    <asp:Button ID="Button_Submit" runat="server" OnClick="Button_Submit_Click" Text="리로드" />
</body>
</html>
cs


[비하인드]

1
2
3
4
5
6
7
8
9
10
11
12
public partial class LoadEventTest : Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        
    }
 
    protected void Button_Submit_Click(object sender, EventArgs e)
    {
        contents.Text = "리로드되었나요?";
    }
}
cs


[참조]

https://docs.microsoft.com/ko-kr/dotnet/api/system.web.ui.updatepanel?view=netframework-4.7.2

https://docs.microsoft.com/en-us/previous-versions/bb383829%28v%3dvs.100%29

ASP.NET MVC에서 Linq to Sql 또는 EF(Entity Framework)를 이용해 DataTables를 개발하는 방법에 대해 작성하려 한다.
이 글에서는 Linq to Sql로 설명 하지만, 구현하는 방법은 EF도 크게 다르지 않을것 같다는 생각이 든다.


DataTables를 간단하게 설명하자면 데이터를 테이블로 표현할때 사용되는 jquery 라이브러리로 페이징, 정렬, 검색, 커스터마이징 등 다양한 기능을 지원하여 손쉽게 개발할 수 있다.


Linq to SqlEF의 아주 간단한 차이점을 말하자면 아래와 같다.

1. Linq to Sql은 MS-SQL만 지원하지만 EF는 대부분의 DB엔진을 지원한다.
2. Linq to Sql은 DB First이며, EF는 DB First, Code First 선택 가능하다. (DB First, Code First에 대한 개념은 인터넷을 찾아보세요.)


구현에 앞서 Linq to Sql의 간단한 사용법을 먼저 확인하고 넘어가자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// DB Context 객체 선언
dbDataContext db = new dbDataContext();
 
// 다음과 같은 Query를 Linq로 표현해보자.
// Account라는 테이블에서 전화번호 1111을 포함한 홍길동이라는 사람을 찾는다.
// SELECT * FROM Account WHERE name = '홍길동' AND phone like '%1111%'
// Linq를 사용하는 방법은 크게 두가지가 있다.
 
// 1. 쿼리 형식 (자세한 사용법은 인터넷을 찾아보세요.)
 
// 반환형은 IQueryable<Account>이다.
var data = from d in db.Account
           where d.name == "홍길동" && d.phone.Contains("1111")
           select d;
 
// 2. 함수 형식
// 반환형은 위와 같이 IQueryable<Account>이다.
var data = db.Account.where(a => a.name == "홍길동" && a.phone.Contains("1111"));
cs


DataTables 공식 홈페이지


1. Quick Start
    1) Html을 작성한다.
    2) DataTables의 Javascript, CSS 파일을 CDN 혹은 다운로드 하여 참조 한다.
    3) Javascript 소스에서 DataTables 초기화

* 아래 소스 참조

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<!-- DataTables CDN -->
<link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/1.10.19/css/jquery.dataTables.css">
<script type="text/javascript" charset="utf8" src="https://cdn.datatables.net/1.10.19/js/jquery.dataTables.js"></script>
 
<table id="table_1">
    <thead>
        <tr>
            <th>col1</th>
            <th>col2</th>
            <th>col3</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>Row 1 Data 1</td>
            <td>Row 1 Data 2</td>
            <td>Row 1 Data 3</td>
        </tr>
        <tr>
            <td>Row 2 Data 1</td>
            <td>Row 2 Data 2</td>
            <td>Row 2 Data 3</td>
        </tr>
        <tr>
            <td>Row 3 Data 1</td>
            <td>Row 4 Data 2</td>
            <td>Row 5 Data 3</td>
        </tr>
    </tbody>
</table>
 
<script>
    $(document).ready( function () {
        $('#table_1').DataTable();
    });
</script>
cs


! DataTables 사용시 데이터를 어디서 처리하는지에 따라 Client-side와 Server-side로 기능을 구현할 수 있다.


2. Client-side processing
    1)  Client-side의 경우 Quick Start와 동일하다. 
        페이지가 열렸을때 Html Table을 작성한 뒤 Script로 초기화만 하면 끝난다.

* 아래 소스 참조 (ASP.NET MVC Razor 구문을 사용하였다.)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<!-- DataTables CDN -->
<link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/1.10.19/css/jquery.dataTables.css">
<script type="text/javascript" charset="utf8" src="https://cdn.datatables.net/1.10.19/js/jquery.dataTables.js"></script>
 
<table id="table_1">
    <thead>
        <tr>
            <th>이름</th>
            <th>전화번호</th>
            <th>col3</th>
        </tr>
    </thead>
    <tbody>
        @foreach(var item in Model)
        {
            <tr>
                <td>item.name</td>
                <td>item.phone</td>
                <td>item.address</td>
            </tr>
        }
    </tbody>
</table>
 
<script>
    $(document).ready( function () {
        $('#table_1').DataTable();
    });
</script>
cs


3. Server-side processing (참조 https://datatables.net/manual/server-side)
    1) Server-side의 경우 DataTables의 옵션으로 쉽게 구현이 가능하다.
    2) Server딴에 데이터를 얻어오기 위한 REST를 구현해야한다.


* 구현시 주의 사항
    -> ajax 옵션에서 컬럼을 지정할 수 있다.
    -> 컬럼을 지정하지 않을 시 컬럼 순서대로 데이터가 매칭된다.

* 아래 소스 참조 (Client딴)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<!-- DataTables CDN -->
<link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/1.10.19/css/jquery.dataTables.css">
<script type="text/javascript" charset="utf8" src="https://cdn.datatables.net/1.10.19/js/jquery.dataTables.js"></script>
 
<table id="table_1">
    <thead>
        <tr>
            <th>이름</th>
            <th>전화번호</th>
            <th>주소</th>
        </tr>
    </thead>
    <tbody>
    </tbody>
</table>
 
<script>
    $(document).ready( function () {
        $('#table_1').DataTable({
            serverSide: true// Server-side 활성화
            ajax: {
                url: '@Url.Action("GetAccounts")'// 데이터를 얻어오기 위한 EndPoint Url
                type: 'POST'
            },
            columns: [
                { "data""name" },
                { "data""phone" },
                { "data""address" }
            ]
        });
    });
</script>
 
cs


* 아래 소스 참조 (Server딴)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
[HttpPost]
public async JsonResult GetAccounts(DataTablesModel model)
{
    try
    {
        using(dbDataContext db = new dbDataContext())
        {
            var accounts = from d in db.Account
                           where d.name.Contains(model.search.value) && d.phone.Contains(model.search.value)
                           select new 
                           {
                                name = d.name,
                                phone = d.phone,
                                address = d.address
                           };
 
 
            //정렬은 직접 구현해보세요.
            // hint
            // 1. model.order[].column의 값이 정렬할 column 명입니다.
            // 2. model.order[].dir의 값이 정렬 방법입니다. desc or asc
            // 3. 멀티 정렬도 가능합니다.
            
            // 페이징 기능 구현
            int pageLength = model.length == -1 ? accounts.Count() : model.length;
            accounts = accounts.Skip(model.start).Take(pageLength);
 
            return Json(new
            {
                draw = model.draw,
                recordsTotal = db.Account.Count(),
                recordsFiltered = accounts.Count(),
                data = accounts, // 이때 DB에서 Query를 실행하여 데이터를 가져온다.
                error = false
            });
        }
    }
    catch(Exception ex)
    {
        return Json(new
        {
            draw = model.draw,
            recordsTotal = 0,
            recordsFiltered = 0,
            data = "",
            error = ex.ToString()
        });        
    }
}
 
#region DataTables Request Model
public class DataTablesModel
{
    public int draw { get; set; }
    public int start { get; set; } // 시작 페이지
    public int length { get; set; } // 페이지에 보여질 Row의 수
    public List<Column> columns { get; set; }
    public Search search { get; set; }
    public List<Order> order { get; set; }
}
 
public class Column
{
    public string data { get; set; }
    public string name { get; set; }
    public bool searchable { get; set; }
    public bool orderable { get; set; }
    public Search search { get; set; }
}
 
public class Search
{
    public string value { get; set; }
    public string regex { get; set; }
}
 
public class Order
{
    public int column { get; set; }
    public string dir { get; set; }
}
#endregion
cs


끗.

80040154 클래스가 등록되지 않았습니다. <- 이 에러는 무엇인가...

사용하려는 DLL의 비트수와 프로그램간의 비트수가 맞지 않아 발생하는 에러인듯 하다.


80040154 클래스가 등록되지 않았습니다. (예외가 발생한 HRESULT: 0x80040154 (REGDB_E_CLASSNOTREG)) 오류로 인해 CLSID가 {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}인 구성 요소의 COM 클래스 팩터리를 검색하지 못했습니다.!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!


빌드시 x86, 혹은 x64로 DLL과 동일한 비트수로 빌드를 하면 문제가 해결된다기에 해봤지만 IIS 에서는 해결방법이 되질 못했다.


IIS에서는 설정을 변경해줘야한다.



아래와 같은 순서로 설정을 변경하면 문제가 해결된다.
64비트 Windows Server 기준으로 IIS는 기본적으로 64비트로 동작한다.
내가 사용하려는 DLL이 32비트였던 것이다.....................


위와 같이 "32비트 응용 프로그램 사용"을 True로 변경하면 문제가 해결된다.


끝.

기본적인 사용법은 teayo에서 보고 습득


간략하게 사용순서를 설명하자면 이렇다.

1. 프로젝트 생성

2. Nuget에서 SignalR 설치

3. Startup Class생성

4. Hub Class 생성

5. javascript에서 signalr 연결 후 사용


tip

1. callback기능이 가능하다. asp.net forum에 잘 나와있다.

2. Comet 방식을 선택할 수 있다. https://www.asp.net/signalr/overview/getting-started/introduction-to-signalr 참조

* 해당 이미지는 https://www.asp.net/signalr/overview/getting-started/introduction-to-signalr 발췌 하였습니다.


+ Recent posts