41. [ADO.NET] DataSet

DataSet은 ADO.NET 핵심 구성 요소로 프로그램 내의 논리적 DB로 데이터 집합과 관계로 구성합니다.

DataTable은 메모리 상의 하나의 테이블을 표현하는 개체이고 DataSet은 메모리 상의 DB를 표현하는 개체입니다. 따라서 DataSet은 여러 개의 테이블과 관계들의 집합체라고 볼 수 있습니다.

DataSet 개체를 구성할 DataTable 개체들은 Tables 컬렉션에 보관하고 관계는 Relations 컬렉션에 보관합니다. DataSet 개체도 DataTable 처럼 스키마와 데이터를 저장하거나 읽어와서 구성하는 메서드를 제공합니다.

DataSet 클래스에는 DataSet 개체를 생성할 때 사용하는 생성자를 제공합니다.

public DataSet ( );
public DataSet ( string name);

DataSet 개체를 생성한 후에는 DB를 구성할 테이블과 관계를 정의할 수 있습니다. 이러한 작업을 DB 설계라고 말합니다.

DB를 설계할 때는 DB에 포함할 테이블을 정의하여 DataSet에 추가하고 서로 다른 테이블에 있는 열 간의 관계를 정의하여 DataSet 개체에 추가합니다.

DataTable dt_publishers = new DataTable("Publishers");
PublishersTableDesign(dt_publishers);
ds.Tables.Add(dt_publishers);
DataTable dt_books = new DataTable("Books");
BooksTableDesign(dt_books);
ds.Tables.Add(dt_books);

DataColumn dc_publishers_cid = dt_publishers.Columns["CID"];
DataColumn dc_books_cid = dt_books.Columns["CID"];
DataRelation dr = new DataRelation("도서출판", dc_publishers_cid, dc_books_cid);
ds.Relations.Add(dr);

다음은 출판사, 도서 테이블을 갖는 DataSet을 디자인하고 간단하게 데이터를 추가하는 소스 코드입니다.

using System;
using System.Data;

namespace DB_설계_및_데이터추가
{
    class Program
    {
        static void Main(string[] args)
        {
            DataSet ds = new DataSet("Library");
            DataSetDegign(ds);

            AddPublisher(ds,"언제나휴일");
            AddPublisher(ds,"언제나평일");

            AddBooks(ds, "언제나휴일", "111-11-1111-111-1", "ADO.NET", "홍길동", 12000);
            AddBooks(ds, "언제나평일", "111-12-1111-111-1", "XML.NET", "강감찬", 15000);
            AddBooks(ds, "언제|나휴일", "111-11-1111-111-2", "Enterprise Service", "홍길동", 20000);
            AddBooks(ds, "언제나평일", "111-12-1111-111-2", "C#", "이기백", 15000);

            View(ds);
        }

        private static void View(DataSet ds)
        {
            Console.WriteLine(ds.DataSetName);

            ViewTables(ds.Tables);
            ViewRelations(ds.Relations);
        }
        private static void ViewRelations(DataRelationCollection drc)
        {
            Console.WriteLine("관계 수:{0}", drc.Count);
            foreach (DataRelation dr in drc)
            {
                Console.WriteLine("관계명:{0}", dr.RelationName);
                DataColumn dc_parent = dr.ParentColumns[0];
                DataColumn dc_child = dr.ChildColumns[0];
                Console.WriteLine("부모:{0}-{1}",dc_parent.Table.TableName, dc_parent.ColumnName);
                Console.WriteLine("자식:{0}-{1}", dc_child.Table.TableName, dc_child.ColumnName);
            }
        }
        private static void ViewTables(DataTableCollection dtc)
        {
            Console.WriteLine("테이블 수:{0}", dtc.Count);
            foreach (DataTable dt in dtc)
            {
                ViewTables(dt);
            }
        }

        private static void ViewTables(DataTable dt)
        {
            int row_count = dt.Rows.Count;
            Console.WriteLine("테이블 명: {0}", dt.TableName);
            Console.WriteLine("행 개수:{0}", row_count);
            DataRow dr = null;
            
            for (int i = 0; i < row_count; i++)
            {
                dr = dt.Rows[i];
                foreach (DataColumn dc in dt.Columns)
                {
                    Console.WriteLine("{0}:{1}", dc.ColumnName, dr[dc]);
                } 
            }
        }
        static void AddBooks(DataSet ds, string cname, string isbn, string title, string author, int price)
        {
            try
            {
                DataTable dt = ds.Tables["Books"];
                int cid = GetCID(ds, cname);
                if (cid == -1)
                {
                    throw new ApplicationException("존재하지 않는 출판사");
                }
                DataRow dr = dt.NewRow();
                dr["ISBN"] = isbn;
                dr["Title"] = title;
                dr["Author"] = author;
                dr["Price"] = price;
                dr["CID"] = cid;
                dt.Rows.Add(dr);
            }
            catch (Exception e)
            {
                Console.WriteLine("{0} 추가 실패", title);
                Console.WriteLine("이유:{0}", e.Message);
            }
        }

        private static int GetCID(DataSet ds, string cname)
        {
            DataTable dt = ds.Tables["Publishers"];
            foreach (DataRow dr in dt.Rows)
            {
                if (cname == dr["CompanyName"])
                {
                    return (int)dr["CID"];
                }
            }
            return -1;
        }
        private static void AddPublisher(DataSet ds, string cname)
        {
            try
            {
                DataTable dt = ds.Tables["Publishers"];

                DataRow dr = dt.NewRow();
                dr["CompanyName"] = cname;
                dt.Rows.Add(dr);
            }
            catch (Exception e)
            {
                Console.WriteLine("{0} 출판사 추가 실패", cname);
                Console.WriteLine("이유:{0}", e.Message);
            }
        }

        private static void DataSetDegign(DataSet ds)
        {
            DataTable dt_publishers = new DataTable("Publishers");
            PublishersTableDesign(dt_publishers);
            ds.Tables.Add(dt_publishers);

            DataTable dt_books = new DataTable("Books");
            BooksTableDesign(dt_books);
            ds.Tables.Add(dt_books);
           
            DataColumn dc_publishers_cid = dt_publishers.Columns["CID"];
            DataColumn dc_books_cid = dt_books.Columns["CID"];

            DataRelation dr = new DataRelation("도서출판", dc_publishers_cid, dc_books_cid);
            ds.Relations.Add(dr);
        }

        private static void PublishersTableDesign(DataTable dt)
        {
            DataColumn dc_cname = new DataColumn();
            dc_cname.ColumnName = "CompanyName";
            dc_cname.DataType = typeof(string);
            dc_cname.AllowDBNull = false;
            dt.Columns.Add(dc_cname);
            DataColumn dc_cid = new DataColumn("CID", typeof(int));
            dc_cid.Unique = true;
            dc_cid.AllowDBNull = false;
            dc_cid.AutoIncrement = true;
            dc_cid.AutoIncrementSeed = 1;
            dc_cid.AutoIncrementStep = 1;
            dt.Columns.Add(dc_cid);

            DataColumn[] pkeys = new DataColumn[1];
            pkeys[0] = dc_cid;
            dt.PrimaryKey = pkeys;
        }

        private static  void BooksTableDesign(DataTable dt)
        {
            DataColumn dc_title = new DataColumn();
            dc_title.ColumnName = "Title";
            dc_title.DataType = typeof(string);
            dc_title.AllowDBNull = false;
            dt.Columns.Add(dc_title);

            DataColumn dc_isbn = new DataColumn("ISBN", typeof(string));
            dc_isbn.Unique = true;
            dc_isbn.AllowDBNull = false;
            dt.Columns.Add(dc_isbn);
            DataColumn dc_author = new DataColumn();
            dc_author.ColumnName = "Author";
            dc_author.DataType = typeof(string);
            dc_author.AllowDBNull = false;
            dt.Columns.Add(dc_author);
            DataColumn dc_price = new DataColumn();
            dc_price.ColumnName = "Price";
            dc_price.DataType = typeof(int);
            dc_price.AllowDBNull = false;
            dt.Columns.Add(dc_price);

            DataColumn dc_cid = new DataColumn("CID", typeof(int));            
            dc_cid.AllowDBNull = false;
            dc_cid.DefaultValue = 0;
            dt.Columns.Add(dc_cid);

            DataColumn[] pkeys = new DataColumn[1];
            pkeys[0] = dc_isbn;
            dt.PrimaryKey = pkeys;
        }
    }
}

DataSet 클래스도 DataTable 처럼 DataSet 구조를 스키마 파일로 저장하거나 로딩하는 메서드를 제공하고 있으며 내용을 저장하거나 로딩하는 메서드를 제공하고 있습니다. 사용법이 큰 차이가 없기 때문에 설명을 하지 않겠습니다.