LINQ를 이용하여 Paging 처리한 샘플코드이다.

샘플 코든는 MSDN에서 제공하는 Sample Database - Northwind Orders 테이블 데이터를 이용하였으며,
LINQ to SQL Classes 타입으로 데이터 객체를 생성하여 접근 하였다.

class Program
{
    // LINQ to SQL class
    static OrderDataContext dbContext = new OrderDataContext();

    static void Main(string[] args)
    {
        Paging(1, 50);

        Console.Read();
    }

    public static void Paging(int pageIdx, int rowCnt)
    {
        var query = from orders in dbContext.Orders
                    orderby orders.OrderID
                    select orders.ShipName;

        var result = query.Skip((pageIdx - 1) * rowCnt).Take(rowCnt);
    }
}

 

페이징 처리를 위해서는 시작하는 페이지 번호와 몇개의 데이터 행을 보여줄지 해당 데이터를 읽어와야 한다.
위의 코드 Paging 메소드를 보면 대략 이러하다..

테이블의 데이터를 쿼리한 후 
Skip 확장 메소드를 활용하여 시작하는 행의 번호를 지정(한 페이지에 보여주는 데이터의 수로 행 번호 계산)해 주고,
Take 확장 메소드를 활용하여 그 시작하는 행 번호로부터 몇 개의 데이터를 읽어올지 지정해주면 된다.

해당 코드 분석하면,
첫번째 페이징 처리 경우는 MS-SQL에서의 TOP이 자동 생성되어 쿼리하고,
두번째 이후 페이징 처리는 ROW_NUMBER 함수를 사용하여 인라인 뷰를 생성하고 TOP를 이용하여 페이징 처리를 한다고 하네요.

참고로 ROW_NUMBER 함수는 MS-SQL 2005 상위 버젼에서만 지원합니다.

1. Using a generic delegate
=> foreach (Customer c in Customers.Where(new Func<Customer, bool>(MatchName)) {}

bool MatchName(Customer c) { return c.CustomerID.StartsWith("L"); }

2. Using an anonymous delegate
=> foreach (Customer c in Customers.Where(delegate(Customer c) { return c.CustomerID.StartsWith("L")}) {}

3. Using a lambda expression
=> foreach (Customer c in Customers.Where(c => c.CustomerID.StartsWith("L"))) {}

4. Using as LINQ query expression
=> var query = from c in Customers where c.CustomerID.StartsWith("L") orderby c.CustomerID select c;

foreach (Customer c in query) { }


ArrayList는 값을 object형식으로 방식해서 받게 되어 모든 타입을 담을 수 있다.

하지만 Boxing이 일어나는데 이러한 빈번한 Boxing을 막기 위해 우리는 Generic에 있는 List<type>을 이용한다.

다음은 Boxing처리되는 ArrayList와 Boxing되지 않는 Generic List<>의 비교를 보도록 한다.

using System;
using System.Collections;
using System.Collections.Generic;

namespace ConsoleApplication1
{
    struct RGB
    {
        public int red;
        public int green;
        public int blue;

        public RGB(int red, int green, int blue)
        {
            this.red = red;
            this.green = green;
            this.blue = blue;
        }

        public override string ToString()
        {
            return red.ToString("X") + green.ToString("X") + blue.ToString("X");
        }
    }

    class Program
    {
        static ArrayList boxValue;
        static List noBoxValue;

        static void Main(string[] args)
        {
            DateTime start;
            DateTime middle;
            TimeSpan end;

            RGB rgb = new RGB(255, 255, 255);

            #region None Boxing

            Console.WriteLine("No Boxing : List.Add(RGB)");

            noBoxValue = new List();

            start = DateTime.Now;

            for (int i = 0; i < 10000000; i++)
                noBoxValue.Add(rgb);

            foreach (RGB value in noBoxValue)
            {
                string str = value.ToString();
            }

            middle = DateTime.Now;
            end = middle - start;

            Console.WriteLine("시작 = {0}, 끝 = {1}, 경과시간 = {2}", start.ToString("hh:mm:ss"),
                middle.ToString("hh:mm:ss"),
                end.ToString());

            #endregion

            #region Boxing

            Console.WriteLine("Boxing : ArrayList.Add(object)");

            boxValue = new ArrayList();

            start = DateTime.Now;

            for (int i = 0; i < 10000000; i++)
                boxValue.Add(rgb);

            foreach (RGB value in boxValue)
            {
                string str = value.ToString();
            }

            middle = DateTime.Now;
            end = middle - start;

            Console.WriteLine("시작 = {0}, 끝 = {1}, 경과시간 = {2}", start.ToString("hh:mm:ss"),
                middle.ToString("hh:mm:ss"),
                end.ToString());

            #endregion

            Console.Read();
        }
    }
}



Finding Unmatched Records in Dataset Tables Using Linq
http://www.eggheadcafe.com/tutorials/aspnet/9b443d4a-0ec3-4599-be41-d4454c1fbd9b/finding-unmatched-records.aspx

1. LINQ(Language Integrated Query )
http://msdn.microsoft.com/ko-kr/library/bb397926.aspx
쿼리를 이용하여 개체와 데이터 간 격차를 줄여주는 기술.
객체지향 기술을 사용하여 네이티브에 정의되지 않은 정보 접근이나 통합의 복잡함을 경감시키는 방식으로  모든 Data소스에 대해 쿼리가 가능하게 한 framework  
주요장점들
   -  여러 조건을 필터링할 때 더욱 명료하며 읽기 쉽다.
   -  최소한의 응용 프로그램 코드를 사용하여 강력한 필터링, 정렬 및 그룹화 기능을 제공.
   -  거의 수정하지 않거나 약간만 수정하여 다른 데이터 소스에 이식할 수 있다.

1.1 다양한 종류의 데이터 소스와 형식에서 작업하기 위한 일관된 모델을 제공

1.1.1 쿼리 가능한 형식 
   - IEnumerable 또는 IEnumerable<(Of <(T>)>)
   - IQueryable<(Of <(T>)>) 같은 파생 인터페이스를 지원하는 형식
   - SQL Server 데이터베이스, XML 문서, ADO.NET 데이터 집합 

1.2. 코딩시 기본 쿼리 작업 특징들

// Data source.
int[] numbers = new int[7] { 0, 1, 2, 3, 4, 5, 6 };
1.2.0 쿼리생성(Query creation)
// var 키워드는 from 절에 지정된 데이터 소스를 검사하여 쿼리 변수의 형식을 추론하도록  컴파일러에 지시
// numQuery변수는는 이 쿼리식만 가지고 실행은 되지 않는다.
var numQuery =  
        from num in numbers where (num % 2) == 0
        select num;

1.2.1. 지연실행
foreach문 사용 시 실재로 실행을함
        foreach (int num in numQuery)
            Console.Write("{0,1} ", num);
1.2.2. 즉시실행강제
-  Count, Max, Average 및 First와 같은 집계함수를 수행 할 때는 내부적으로 내장 foreach문이 실행됨
       int numCount = numQuery .Count();
1.2.3.  실행결과 캐싱
-  ToList<(Of <(TSource>)>) 또는 ToArray<(Of <(TSource>)>) 메서드를 호출 결과를 캐싱
         List<int> numQuery2 = (from num in numbers where (num % 2) == 0 select num).ToList();
        // or like this:
        var numQuery3 = (from num in numbers where (num % 2) == 0 select num).ToArray();
1.2.4.
정렬
      var queryLondonCustomers3 = 
             from cust in customers where cust.City == "London"
            
orderby cust.Name ascending
             select cust;
1.2.5. 그룹화
-
group 절은 그룹의 키 값과 일치하는 하나 이상의 항목을 포함하는 IGrouping<(Of <(TKey, TElement>)>) 개체 시퀀스를 반환
      // Query variable is an IEnumerable<IGrouping<char, Student>>
      var studentQuery1 =
      from student in students
      group student by student.Last[0]; //char형 키를 저장하게된다.

1.2.5.1. 각 그룹에서 추가 쿼리 작업을 수행하려면  into 컨텍스트키워드를 이용하여 임시식별자를 만들어서 수행
      // custQuery is an IEnumerable<IGrouping<string, Customer>>
      var custQuery = from cust in customers
      group cust by cust.City into custGroup
      where
custGroup.Count() > 2
      orderby custGroup.Key
      select custGroup;

1.2.5.2. 그룹쿼리 결과얻기
      // Iterate group items with a nested foreach. This IGrouping encapsulates a sequence of Student objects, and a Key of type char.
      // For convenience, var can also be used in the foreach statement.
       foreach (IGrouping<char, Student> studentGroup in studentQuery2)
      {
            Console.WriteLine(studentGroup.Key);
            // Explicit type for student could also be used here.
            foreach (var student in studentGroup)
           {
                Console.WriteLine("   {0}, {1}", student.Last, student.First);
           }
       }
       
1.2.5.3. 그루핑 시 키 지정
        그룹 키는 문자열, 기본 제공 숫자 형식 또는 사용자 정의 명명된 형식이나 익명 형식과 같은 임의의 형식가능

1.2.5.3.1. 문자열값으로 그룹화예
 Query variable(studentQuery3 ) is an IEnumerable<IGrouping<string, Student>> 이 됨
 var studentQuery3 = from student in students group student by student.Last;

1.2.5.3.2. 부울값으로 그룹화 예
 group by 다음에 조건을 지정하여 조건에 해당하는 두 그룹으로 나누는 경우
 데이타가 아래와 같이 정의된경우,
         List<Student> students = new List<Student>
        {
           new Student {First="Svetlana", Last="Omelchenko", ID=111, Scores= new List<int> {97, 72, 81, 60}},
           new Student {First="Claire", Last="O'Donnell", ID=112, Scores= new List<int> {75, 84, 91, 39}},
           new Student {First="Sven", Last="Mortensen", ID=113, Scores= new List<int> {99, 89, 91, 95}},
           new Student {First="Cesar", Last="Garcia", ID=114, Scores= new List<int> {72, 81, 65, 84}},
           new Student {First="Debra", Last="Garcia", ID=115, Scores= new List<int> {97, 89, 85, 82}}
        };
 다음과 같이 조건으로 그루핑하면 키값이 boolean형으로 저장됨
         var booleanGroupQuery =
            from student in students
            group student by student.Scores.Average() >= 80; //pass or fail!

        // Execute the query and access items in each group
        foreach (var studentGroup in booleanGroupQuery)
        {
            Console.WriteLine(studentGroup.Key == true ? "High averages" : "Low averages");
            foreach (var student in studentGroup)
            {
                Console.WriteLine("   {0}, {1}:{2}", student.Last, student.First, student.Scores.Average());
            }
        }
1.2.5.3.3. 숫자범위로 그룹화 예
 group by 다음에 범위형태로 조건을 줄 수도 있다. 10단위로 키값이 그루핑되나봅니다.
        var studentQuery =
            from student in students
            let avg = (int)student.Scores.Average()
            group student by (avg == 0 ? 0 : avg / 10) into g
            orderby g.Key
            select g;           

        // Execute the query.
        foreach (var studentGroup in studentQuery)
        {
            int temp = studentGroup.Key * 10;
            Console.WriteLine("Students with an average between {0} and {1}", temp, temp + 10);
            foreach (var student in studentGroup)
            {
                Console.WriteLine("   {0}, {1}:{2}", student.Last, student.First, student.Scores.Average());
            }
        }
1.2.5.3.3. 복합키로 그룹화 예
group person by new {name = person.surname, city = person.city};

1.2.6. 조인

1.2.6.1. 내부조인
  var innerJoinQuery =
  from category in categories
            join prod in products on category.ID equals prod.CategoryID
  select new { ProductName = prod.Name, Category = category.Name }; //produces flat sequence

1.2.6.2. 그룹조인
  - into 식이 있는 join 절을 그룹 조인을 말한다. T-Sql에 이런게 없는걸로 아는데... 재미난 조인인듯
  - 그룹 조인은 왼쪽 소스 시퀀스의 요소를 오른쪽 소스 시퀀스에 있는 하나 이상의 일치하는 요소와 연결하는 계층적 결과를 생성
  - 왼쪽 소스의 요소와 일치하는 오른쪽 소스 시퀀스의 요소가 없을 경우 join 절은 해당 항목에 대해 빈 배열을 생성
  - 그룹 조인은 결과 시퀀스가 그룹으로 구성된다는 점을 제외하고 기본적으로 내부 동등 조인과 같다?
    var innerGroupJoinQuery = 
    from category in categories  
               join prod in products on category.ID equals prod.CategoryID
into prodGroup
    select new { CategoryName = category.Name, Products = prodGroup };
  - 그룹 조인의 결과를 다른 하위 쿼리의 생성기로 사용 (T-Sql에서 중첩쿼리에 해당하겠네요)
    var innerGroupJoinQuery2 =
    from category in categories
               join prod in products on category.ID equals prod.CategoryID into prodGroup
    from prod2 in prodGroup
    where prod2.UnitPrice > 2.50M
    select prod2;   

1.2.6.3.
왼쪽우선 외부조인 (Tsql의 Left Outer Join)
  - LINQ에서 왼쪽 우선 외부 조인을 수행하려면 DefaultIfEmpty 메서드를 그룹 조인과 함께 사용하여
    왼쪽 요소에 일치 항목이 없을 경우 생성할 기본 오른쪽 요소를 지정
    문법이 매끄럽지가 못하다는 느낌이...(그냥 Left Join으로 표기하지 않구.. 엄청 어렵게 표현T.T)
          var leftOuterJoinQuery =
          from category in categories
                   join prod in products on category.ID equals prod.CategoryID
into prodGroup
          from item in prodGroup.DefaultIfEmpty(new Product{Name = String.Empty, CategoryID = 0})
          select new { CatName = category.Name, ProdName = item.Name };

1.2.6.4. 개체 컬렉션 및 관계형 테이블의 조인
         LINQ 쿼리 식에서는 조인 작업이 개체 컬렉션에서 수행되며 두 개의 관계형 테이블과 동일한 방식으로 "조인"할 수 없다.
         LINQ에서 명시적 join 절은 두 개의 소스 콜렉션이 어떤 관계로도 연결되지 않은 경우에만 필요
         LINQ to SQL을 사용하는 경우 외래 키 테이블은 개체 모델에 기본 테이블의 속성으로 표현되므로 조인이 이미 된 상태임
        ( 예를 들어 Northwind 데이터베이스의 Customer 테이블은 Orders 테이블과 외래 키 관계가 있습니다.
          테이블을 개체 모델에 매핑하면 Customer 클래스에 해당 Customer와 연결된 Orders 컬렉션을 포함하는 Orders 속성이 자동생성됨)

 1.2.6.5. 복합키 조인 
         익명 형식의 복합 키를 만들거나, 비교할 값이 포함된 명명된 형식으로 복합 키를 만듬.
        (참고: db라는 collection의 속성으로 Orders와 Products가 이미 포함되었다고 가정함)
         var query = 
         from o in db.Orders
         from p in db.Products
                          join d in db.OrderDetails on new {o.OrderID, p.ProductID} equals new {d.OrderID,        d.ProductID}
         into details
         from d in details
         select new {o.OrderID, p.ProductID, d.UnitPrice};    

1.3. LINQ를 통한 데이터 변환
 LINQ(통합 언어 쿼리)는 데이터를 검색할 뿐 아니라 데이터를 변환하는 강력한 도구.
 LINQ 쿼리를 사용하면 소스 시퀀스를 입력으로 사용하고 다양한 방식으로 수정하여 새 출력 시퀀스를 만들 수 있다.
 LINQ 쿼리의 가장 강력한 기능은 select절을 이용한 새 형식을 만드는 기능

1.3.1. 여러 입력을 하나의 출력 시퀀스로 결합 
두 개의 메모리 내 데이터 구조를 결합하는 방법을 보여 주지만, XML이나 SQL 또는 DataSet 소스의 데이터를 결합하는 경우에도 동일한 원칙을 적용할 수 있다.        
        // Create the first data source.
        List<Student> students = new List<Student>()
        {
            new Student {First="Svetlana", Last="Omelchenko",  ID=111, Street="123 Main Street", City="Seattle", Scores= new List<int> {97, 92, 81, 60}},
            new Student {First="Claire", Last="O’Donnell",  ID=112, Street="124 Main Street", City="Redmond", Scores= new List<int> {75, 84, 91, 39}},
            new Student {First="Sven", Last="Mortensen", ID=113, Street="125 Main Street", City="Lake City", Scores= new List<int> {88, 94, 65, 91}},
        };

        // Create the second data source.
        List<Teacher> teachers = new List<Teacher>()
        {               
            new Teacher {First="Ann", Last="Beebe", ID=945, City = "Seattle"},
            new Teacher {First="Alex", Last="Robinson", ID=956, City = "Redmond"},
            new Teacher {First="Michiyo", Last="Sato", ID=972, City = "Tacoma"}
        };

        // Create the query.
        var peopleInSeattle =
        (
           from student in students
           where student.City == "Seattle"
           select student.Last
        )
        .Concat  // 쿼리의 union all 에 해당
       (
          from teacher in teachers
          where teacher.City == "Seattle"
          select teacher.Last
       );

        Console.WriteLine("The following students and teachers live in Seattle:");
       
        // Execute the query.
        foreach (var person in peopleInSeattle)
        {
            Console.WriteLine(person);
        }

1.3.2. 각 소스 요소의 하위 집합 선택 
 - 단순한 Select
    var query = from cust in Customers
            select cust.City;
 - 새로운 인스턴트스로 생성하며 select
   var query = from cust in Customer
            select new {Name = cust.Name, City = cust.City}; 

1.3.3. 메모리 내부 개체를 XML로 변환 
// Create the data source by using a collection initializer.
        List<Student> students = new List<Student>()
        {
            new Student {First="Svetlana", Last="Omelchenko", ID=111, Scores = new List<int>{97, 92, 81, 60}},
            new Student {First="Claire", Last="O’Donnell", ID=112, Scores = new List<int>{75, 84, 91, 39}},
            new Student {First="Sven", Last="Mortensen", ID=113, Scores = new List<int>{88, 94, 65, 91}},
        };

        // Create the query. 이렇게 XElement를 이용하여~~~
        var studentsToXML =
        new XElement("Root",
            from student in students
            let x = String.Format("{0},{1},{2},{3}", student.Scores[0], student.Scores[1], student.Scores[2], student.Scores[3])
            select new XElement("student",
                       new XElement("First", student.First),
                       new XElement("Last", student.Last),
                       new XElement("Scores", x)
                    ) // end "student"
                ); // end "Root"

        // Execute the query.
        Console.WriteLine(studentsToXML);

1.3.4.  소스 요소에서 작업 수행
        // Data source.
        double[] radii = { 1, 2, 3 };

        // Query.
        IEnumerable<string> query =
            from rad in radii
            select String.Format("Area = {0}", (rad * rad) * 3.14);

        // Query execution.
        foreach (string s in query)
            Console.WriteLine(s);

1.4. 쿼리 구문과 메서드 구문 비교
NET CLR(공용 언어 런타임) 자체에는 쿼리 구문의 개념이 없고 컴파일 타임에 쿼리 식은 CLR이 인식가능한 메서드 호출로 변환됨
이러한 메서드를 표준 쿼리 연산자 라고 하며 Where, Select, GroupBy, Join, Max, Average,... 등이 있다.
쿼리 구문 대신 메서드 구문을 사용하여 이를 직접 호출할 수 있다.
          int[] numbers = { 5, 10, 8, 3, 6, 12};
         //Query syntax:
        IEnumerable<int> numQuery1 =
            from num in numbers
            where num % 2 == 0
            orderby num
            select num;

        //Method syntax:
        IEnumerable<int> numQuery2 = numbers.Where(num => num % 2 == 0).OrderBy(n => n);
둘의 결과는 동일하다.
Where(num => num % 2 == 0) 이 인라인 식을 람다식 이라한다.
람다식을 사용하지 않을 경우 익명 메서드나 제네릭 대리자 또는 식 트리와 같은 형식으로 코드를 작성해야 한다.
C#에서 =>는 "goes to"를 나타내는 람다 연산자
 

1.5. 람다식
http://msdn.microsoft.com/ko-kr/library/bb397687.aspx 계속
http://msdn.microsoft.com/ko-kr/library/bb397951.aspx 식트리
http://msdn.microsoft.com/ko-kr/library/bb882636.aspx
식과 문을 포함하고 대리자나 식 트리 형식을 만드는 데 사용할 수 있는 익명 함수
모든 람다 식에는 람다 연산자 =>("이동"이라고 읽음)사용
왼쪽에는 입력 매개 변수(있는 경우)를 지정하고 오른쪽에는 식 또는 문 블록이 위치
=> 연산자는 할당 연산자(=)와 우선 순위가 같으며 오른쪽 결합성이 있다.
예) x => x * x라는 람다 식은 "x는 x 곱하기 x로 이동"으로 읽으며, 이 식은 다음과 같이 대리자 형식에 할당할 수 있다.
delegate int del(int i);
del myDelegate = x => x * x;
int j = myDelegate(5); //j = 25

메서드 기반 LINQ 쿼리에서 Where 및 Where 같은 표준 쿼리 연산자 메서드의 인수로 사용.
LINQ to Objects 및 LINQ to XML에서처럼 메서드 기반의 구문을 사용하여 Enumerable 클래스에서 Where 메서드를 호출하는 경우
매개 변수는 System..::.Func<(Of <(T, TResult>)>) 대리자 형식
람다식은 대리자를 만드는 가장 간단한 방법이다.
LINQ to SQL에서 System.Linq..::.Queryable 클래스 에서 호출하는 경우 매개 변수 형식은 System.Linq.Expressions..::.Expression<Func>이고, 여기서 Func는 입력 매개 변수를 5개까지 가질 수 있는 임의의 Func 대리자이다.

1.5.1. 식 람다(expression lamda)
- 오른쪽에 식이 있는 람다 식,  식 트리를 만드는 데 광범위하게 사용,식 람다는 식의 결과를 반환
 (input parameters) => expression
- 괄호는 람다 식에 입력 매개 변수가 하나뿐인 경우에만 생략
- 둘 이상의 입력 매개 변수는 다음과 같이 괄호로 묶고 쉼표로 구분
 (x, y) => x == y
- 컴파일러에서 입력 형식을 유추할 수 없는 경우 다음과 같이 형식을 명시적으로 지정
 (int x, string s) => s.Length > x
- 입력 매개 변수가 0개이면 다음과 같이 빈 괄호를 지정
 () => SomeMethod()
1.5.2.  식트리
System.Linq.Expressions 네임스페이스 필요
IQueryable<(Of <(T>)>)을 구현하는 데이터의 소스를 대상으로 하는 구조화된 쿼리를 나타내기 위해 식 트리를 사용
LINQ에서 Expression<(Of <(TDelegate>)>) 형식 변수에 할당되는 람다 식을 나타내는 데에도 사용
   Expression<Func<int, bool>> exprTree = num => num < 5;
   와같이 Delegate표현대신 Expression클래스 형식을 사용하여 트리형태로 식을저장
ParameterExpression,ConstantExpression,BinaryExpression등과같이 식에표현된 각 부분에 해당하는 클래스들존재

// Manually build the expression tree for the lambda expression num => num < 5.
ParameterExpression numParam = Expression.Parameter(typeof(int), "num");
ConstantExpression five = Expression.Constant(5, typeof(int));
BinaryExpression numLessThanFive = Expression.LessThan(numParam, five);
Expression<Func<int, bool>> lambda1 =
    Expression.Lambda<Func<int, bool>>(
        numLessThanFive,
        new ParameterExpression[] { numParam });
와 동일한 람다식표현
// Let the compiler generate the expression tree for the lambda expression num => num < 5.
Expression<Func<int, bool>> lambda2 = num => num < 5;
이정도 개념만 있어도 충분할듯, 더 자세한 부분은 MSDN을 찾아보시고 http://msdn.microsoft.com/ko-kr/library/bb882637.aspx


1.5.3. 문 람다 (statement lamda)
- 문 람다는 다음과 같이 중괄호 안에 문을 지정한다는 점을 제외하면 식 람다와 비슷
 (input parameters) => {statement;}
- 문 람다의 본문에 지정할 수 있는 문의 개수에는 제한이 없지만 일반적으로 2-3개 정도만 지정
  (문 람다는 식 트리를 만드는 데 사용될 수 없다)
 delegate void TestDelegate(string s);
 …
 TestDelegate myDel = n => { string s = n + " " + "World"; Console.WriteLine(s); };
 myDel("Hello");

1.5.4. 표준 쿼리 연산자와 람다 식
- 대부분의 표준 쿼리 연산자에는 형식이 제네릭 대리자의 Func<(Of <(T, TResult>)>) 패밀리 중 하나인 입력 매개 변수를 사용
 public delegate TResult Func<TArg0, TResult>(TArg0 arg0)
 이 경우 대리자를 Func<int,bool> myFunc로 인스턴스화할 수 있다.
 int는 입력 매개 변수이고, bool은 반환 값(반환 값은 항상 마지막 형식 매개 변수로 지정)

예) 5인지 여부판단하여 bool로 리턴
    Func<int, bool> myFunc = x => x == 5;
    bool result = myFunc(4); // returns false of course
- System.Linq.Queryable에 정의되어 있는 표준 쿼리 연산자의 경우와 같이 인수 형식이 Expression<Func>인 경우에도 람다 식을 사용.
  Expression<Func> 인수를 지정하면 식 트리에 람다 식이 컴파일됨

예) 2로 나누었을 때 나머지가 1인 정수(n)의 수를 계산
      int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };
      int oddNumbers = numbers.Count(n => n % 2 == 1);
    시작부터 검사하여 6보다 작은 값들 추출
      var firstNumbersLessThan6 = numbers.TakeWhile(n => n < 6);
    2개의 파라메타를 입력받아 동적으로 임계치 변경
      var firstSmallNumbers = numbers.TakeWhile((n, index) => n >= index);

1.5.5. 람다 식에서의 형식 유추
입력 매개 변수의 형식은 대부분의 표준 쿼리 연산자에서 첫 번째 입력 형식은 소스 시퀀스 요소의 형식으로 컴파일러가 유추함
IEnumerable<Customer>을 쿼리할 경우 입력 변수가 Customer 개체로 유추
customers.Where(c => c.City == "London");
- 람다 식적용 규칙
  람다 식과 대리자 형식에 포함된 매개 변수 수가 같아야 한다. 
   람다 식의 각 입력 매개 변수는 해당되는 대리자 매개 변수로 암시적으로 변환될 수 있어야 한다.
  람다 식의 반환 값(있는 경우)은 대리자의 반환 형식으로 암시적으로 변환될 수 있어야 한다.
  람다 식의 "형식"을 비공식적으로 언급해야 할 경우 대리자 형식 또는 람다 식이 변환되는 Expression 형식을 의미

1.5.6. 람다 식의 변수 범위
람다 식은 람다 식이 정의된 바깥쪽 메서드나 형식의 범위에 포함되어 있는 외부 변수를 참조할 수 있다.
이러한 방식으로 캡처되는 변수는 변수가 범위를 벗어나 가비지 수집되는 경우에도 람다 식에 사용할 수 있도록 저장된다.
외부 변수는 명확하게 할당해야만 람다 식에 사용할 수 있다
- 람다 식의 변수 범위규칙
  캡처된 변수는 해당 변수를 참조하는 대리자가 범위에서 벗어날 때까지 가비지 수집되지 않는다.
  람다 식에 사용된 변수는 외부 메서드에 표시되지 않는다.
  람다 식은 바깥쪽 메서드에서 ref 또는 out 매개 변수를 직접 캡처할 수 없습니다.
  람다 식의 return 문에 의해서는 바깥쪽 메서드가 반환되지는 않는다.?
  람다 식에는 포함된 익명 함수의 본문 또는 본문 외부를 대상으로 하는 goto 문, break 문 또는 continue 문이 포함될 수 없다

예)
    delegate bool D();
    delegate bool D2(int i);

    class Test
    {
        D del;
        D2 del2;
        public void TestMethod(int input)
        {
            int j = 0;
            // Initialize the delegates with lambda expressions.
            // Note access to 2 outer variables.
            // del will be invoked within this method.
            del = () => { j = 10;  return j > input; };

            // del2 will be invoked after TestMethod goes out of scope.
            del2 = (x) => {return x == j; };
           
            // Demonstrate value of j:
            // Output: j = 0
            // The delegate has not been invoked yet.
            Console.WriteLine("j = {0}", j);

            // Invoke the delegate.
            bool boolResult = del();

            // Output: j = 10 b = True
            Console.WriteLine("j = {0}. b = {1}", j, boolResult);
        }

        static void Main()
        {
            Test test = new Test();
            test.TestMethod(5);

            // Prove that del2 still has a copy of
            // local variable j from TestMethod.
            bool result = test.del2(10);

            // Output: True
            Console.WriteLine(result);
           
            Console.ReadKey();
        }
    }

2. LINQ to Objects
중간 LINQ 공급자나 API는 사용하지 않고 LINQ to SQL,LINQ to XML처럼 IEnumerable,IEnumerable<(Of <(T>)>) 파생 컬렉션을 직접 사용하는 LINQ쿼리를 말함
LINQ를 사용하여 List<(Of <(T>)>), Array 또는 Dictionary<(Of <(TKey, TValue>)>)와 같은 열거 가능한 컬렉션을 모두 쿼리가능

2.1. LINQ 및 문자열
문자열 및 문자열의 컬렉션을 쿼리하고 변환
일반적인 문자열 함수 및 정규식과 결합될 수 있다.

2.1.1 문자열에서 단어가 나오는 횟수 세기(LINQ)
 class CountWords
{
    static void Main()
    {
        string text = @"Historically, the world of data and the world of objects" +
          @" have not been well integrated. Programmers work in C# or Visual Basic" +
          @" and also in SQL or XQuery. On the one side are concepts such as classes," +
          @" objects, fields, inheritance, and .NET Framework APIs. On the other side" +
          @" are tables, columns, rows, nodes, and separate languages for dealing with" +
          @" them. Data types often require translation between the two worlds; there are" +
          @" different standard functions. Because the object world has no notion of query, a" +
          @" query can only be represented as a string without compile-time type checking or" +
          @" IntelliSense support in the IDE. Transferring data from SQL tables or XML trees to" +
          @" objects in memory is often tedious and error-prone.";

        string searchTerm = "data";

        //Convert the string into an array of words
        string[] source = text.Split(new char[] { '.', '?', '!', ' ', ';', ':', ',' }, StringSplitOptions.RemoveEmptyEntries);

        // Create and execute the query. It executes immediately because a singleton value is produced. 즉시실행???
        // Use ToLowerInvariant to match "data" and "Data"
        var matchQuery = from word in source
                         where word.ToLowerInvariant() == searchTerm.ToLowerInvariant()
                         select word;

        // Count the matches.
        int wordCount = matchQuery.Count();
        Console.WriteLine("{0} occurrences(s) of the search term \"{1}\" were found.", wordCount, searchTerm);

    }
}
/* Output:
   3 occurrences(s) of the search term "data" were found.
*/

2.1.2 지정된 단어 집합이 들어 있는 문장 쿼리(LINQ)
//나눈 문장들에서 "Historically", "data" 및 "integrated"이라는 단어를 모두 포함한 문장을 반환.
        // Split the text block into an array of sentences.
        string[] sentences = text.Split(new char[] { '.', '?', '!' });

        // Define the search terms. This list could also be dynamically populated at runtime.
        string[] wordsToMatch = { "Historically", "data", "integrated" };

        // let에서 문장들 나누고, Distinct로 중복된 문장제거, Intersect로 교집합연산의 개수가 wordsToMatch의 개수와 동일한 문장만 Select
        // Note that the number of terms to match is not specified at compile time.
        var sentenceQuery = from sentence in sentences
                            let w = sentence.Split(new char[] { '.', '?', '!', ' ', ';', ':', ',' },StringSplitOptions.RemoveEmptyEntries)
                            where w.Distinct().Intersect(wordsToMatch).Count() == wordsToMatch.Count()
                            select sentence;
     
        // Execute the query. Note that you can explicitly type the iteration variable here even though sentenceQuery
        // was implicitly typed.
        foreach (string str in sentenceQuery)
        {
            Console.WriteLine(str);
        }
2.1.3 문자열의 문자 쿼리(LINQ)
String 클래스는 제네릭 IEnumerable<(Of <(T>)>) 인터페이스를 구현하기 때문에 모든 문자열을 문자 시퀀스로 쿼리할 수 있다.
class QueryAString
{
    static void Main()
    {
        string aString = "ABCDE99F-J74-12-89A";

        // Select only those characters that are numbers 숫자로~~
        IEnumerable<char> stringQuery =
          from ch in aString
          where Char.IsDigit(ch)
          select ch;

        // Execute the query
        foreach (char c in stringQuery)
            Console.Write(c + " ");

        // Call the Count method on the existing query.
        int count = stringQuery.Count();
        Console.WriteLine("Count = {0}", count);

        // Select all characters before the first '-'
        IEnumerable<char> stringQuery2 = aString.TakeWhile(c => c != '-');

        // Execute the second query
        foreach (char c in stringQuery2)
            Console.Write(c);

        Console.WriteLine(System.Environment.NewLine + "Press any key to exit");
        Console.ReadKey();

    }
}
/* Output:
  Output: 9 9 7 4 1 2 8 9
  Count = 8
  ABCDE99F
*/

2.1.4. LINQ 쿼리와 정규식 결합
Regex 클래스를 사용하여 텍스트 문자열에서 좀 더 복잡한 비교를 위해 정규식을 만드는 방법
class QueryWithRegEx
{
     // This method assumes that the application has discovery permissions for all folders under the specified path.
    static IEnumerable<System.IO.FileInfo> GetFiles(string path)
    {
        if (!System.IO.Directory.Exists(path))
            throw new System.IO.DirectoryNotFoundException();

        string[] fileNames = null;
        List<System.IO.FileInfo> files = new List<System.IO.FileInfo>();

        fileNames = System.IO.Directory.GetFiles(path, "*.*", System.IO.SearchOption.AllDirectories);
        foreach (string name in fileNames)
        {
            files.Add(new System.IO.FileInfo(name));
        }
        return files;
    }
    public static void Main()
    {
        // Modify this path as necessary.
        string startFolder = @"c:\program files\Microsoft Visual Studio 9.0\";

        // Take a snapshot of the file system.
        IEnumerable<System.IO.FileInfo> fileList = GetFiles(startFolder); //폴더의 모든 파일정보

        // Create the regular expression to find all things "Visual".
        System.Text.RegularExpressions.Regex searchTerm =
            new System.Text.RegularExpressions.Regex(@"Visual (Basic|C#|C\+\+|J#|SourceSafe|Studio)");

        // Search the contents of each .htm file.
        // Remove the where clause to find even more matches!
        // This query produces a list of files where a match was found, and a list of the matches in that file.
        // Note: Explicit typing of "Match" in select clause.
        // This is required because MatchCollection is not a generic IEnumerable collection.
        var queryMatchingFiles =
            from file in fileList
            where file.Extension == ".htm" //확장자가 htm인 것들중에
            let fileText = System.IO.File.ReadAllText(file.FullName) // 파일명만 fileText 목록(집합)으로두고
            let matches = searchTerm.Matches(fileText) // fileText에서 searchTerm에 해당하는 것들만
            where searchTerm.Matches(fileText).Count > 0 //이게 없으면 matches에 null도 들어가나??? 확인필요
            select new
            {
                name = file.FullName,
                matches = from System.Text.RegularExpressions.Match match in matches
                          select match.Value
            };

        // Execute the query.
        Console.WriteLine("The term \"{0}\" was found in:", searchTerm.ToString());

        foreach (var v in queryMatchingFiles)
        {
            // Trim the path a bit, then write the file name in which a match was found.
            string s = v.name.Substring(startFolder.Length - 1);
            Console.WriteLine(s);

            // For this file, write out all the matching strings
            foreach (var v2 in v.matches)
            {
                Console.WriteLine("  " + v2);
            }
        }
    } 
}
2.1.5. 두 목록 간의 차집합 구하기(LINQ)
 names1.txt에 있지만 names2.txt에는 없는 해당 줄을 출력
class CompareLists
{       
    static void Main()
    {
        // Create the IEnumerable data sources.
        string[] names1 = System.IO.File.ReadAllLines(@"../../../names1.txt");
        string[] names2 = System.IO.File.ReadAllLines(@"../../../names2.txt");

        // Create the query. Note that method syntax must be used here.
        IEnumerable<string> differenceQuery =
          names1.Except(names2);

        // Execute the query.
        Console.WriteLine("The following lines are in names1.txt but not names2.txt");
        foreach (string s in differenceQuery)
            Console.WriteLine(s);
    }
}
2.1.6. 단어 또는 필드에 따라 텍스트 데이터 정렬 또는 필터링(LINQ)
쉼표로 구분된 값과 같은 구조화된 텍스트 줄을 해당 줄에서 임의의 필드를 기준으로 정렬하는 방법
scores.csv의 필드는 학생의 ID 번호와 4개의 테스트 점수를 나타낸다고 가정
public class SortLines
{
    static void Main()
    {
        // Create an IEnumerable data source
        string[] scores = System.IO.File.ReadAllLines(@"../../../scores.csv");

        // Change this to any value from 0 to 4.
        int sortField = 1; //두번째 필드 말하죠.. (0부터시작)

        Console.WriteLine("Sorted highest to lowest by field [{0}]:", sortField);

        // Demonstrates how to return query from a method.
        // The query is executed here.
        foreach (string str in RunQuery(scores, sortField))
        {
            Console.WriteLine(str);
        }
    }

    // Returns the query variable, not query results!
    static IEnumerable<string> RunQuery(IEnumerable<string> source, int num)
    {
        // Split the string and sort on field[num]
        var scoreQuery = from line in source
                         let fields = line.Split(',')
                         orderby fields[num] descending
                         select line;

        return scoreQuery;
    }
}
/* Output (if sortField == 1):
   Sorted highest to lowest by field [1]:
    116, 99, 86, 90, 94
    120, 99, 82, 81, 79
    111, 97, 92, 81, 60
    114, 97, 89, 85, 82
    121, 96, 85, 91, 60
    122, 94, 92, 91, 91
    117, 93, 92, 80, 87
    118, 92, 90, 83, 78
    113, 88, 94, 65, 91
    112, 75, 84, 91, 39
    119, 68, 79, 88, 92
    115, 35, 72, 91, 70
 */
2.1.7. 구분된 파일의 필드 다시 정렬후 파일로 저장.
- spreadsheet1.csv라는 일반 텍스트 내용
Adams,Terry,120
Fakhouri,Fadi,116
Feng,Hanying,117
Garcia,Cesar,114
Garcia,Debra,115
Garcia,Hugo,118
Mortensen,Sven,113
O'Donnell,Claire,112
Omelchenko,Svetlana,111
Tucker,Lance,119
Tucker,Michael,122
Zabokritski,Eugene,121

-파일을 읽어 쉼표로 구분하고,  3번쨰 필드로 정렬한 후 다시 파일로 저장
        // Create the IEnumerable data source
        string[] lines = System.IO.File.ReadAllLines(@"../../../spreadsheet1.csv");

        // Create the query. Put field 2 first, then
        // reverse and combine fields 0 and 1 from the old field
        IEnumerable<string> query =
            from line in lines
            let x = line.Split(',')
            orderby x[2] 
            select x[2] + ", " + (x[1] + " " + x[0]);

        // Execute the query and write out the new file. Note that WriteAllLines
        // takes a string[], so ToArray is called on the query.
        System.IO.File.WriteAllLines(@"../../../spreadsheet2.csv", query.ToArray());

2.1.7. 문자열 컬렉션 결합 및 비교
텍스트 줄이 포함된 파일을 병합한 다음 결과를 정렬하는 방법
특히 두 개의 텍스트 줄 집합에서의 간단한 연결, 공용 구조체 및 교집합을 수행
- names1.txt 내용
Bankov, Peter
Holm, Michael
Garcia, Hugo
Potra, Cristina
Noriega, Fabricio
Aw, Kam Foo
Beebe, Ann
Toyoshima, Tim
Guy, Wey Yuan
Garcia, Debra
- names2.txt 내용
Liu, Jinghao
Bankov, Peter
Holm, Michael
Garcia, Hugo
Beebe, Ann
Gilchrist, Beth
Myrcha, Jacek
Giakoumakis, Leo
McLin, Nkenge
El Yassir, Mehdi

        static void OutputQueryResults(IEnumerable<string> query, string message)
        {
            Console.WriteLine(System.Environment.NewLine + message);
            foreach (string item in query)
            {
                Console.WriteLine(item);
            }
            Console.WriteLine("{0} total names in list", query.Count());
        }
//위 함수가 미리 정의돼 있다고 가정
            //Put text files in your solution folder
            string[] fileA = System.IO.File.ReadAllLines(@"../../../names1.txt");
            string[] fileB = System.IO.File.ReadAllLines(@"../../../names2.txt");

            //Simple concatenation and sort. Duplicates are preserved.
            IEnumerable<string> concatQuery = fileA.Concat(fileB).OrderBy(s => s);

            // Pass the query variable to another function for execution.
            OutputQueryResults(concatQuery, "Simple concatenate and sort. Duplicates are preserved:");

            // Concatenate and remove duplicate names based on default string comparer.(병합)
            IEnumerable<string> uniqueNamesQuery =
                fileA.Union(fileB).OrderBy(s => s); //병합하면서 distinct도 될터
            OutputQueryResults(uniqueNamesQuery, "Union removes duplicate names:");

            // Find the names that occur in both files (based on default string comparer). (교집합)
            IEnumerable<string> commonNamesQuery =
                fileA.Intersect(fileB);
            OutputQueryResults(commonNamesQuery, "Merge based on intersect:");

            // Find the matching fields in each list. Merge the two results by using Concat,
            // and then
sort using the default string comparer.
            string nameMatch = "Garcia";

            IEnumerable<String> tempQuery1 =
                from name in fileA
                let n = name.Split(',')
                where n[0] == nameMatch //,로 분리후 0번필드중에  nameMatch와 일치하는  것들 

                select name;

            IEnumerable<string> tempQuery2 =
                from name2 in fileB
                let n2 = name2.Split(',')
                where n2[0] == nameMatch
                select name2;

            IEnumerable<string> nameMatchQuery =
                tempQuery1.Concat(tempQuery2).OrderBy(s => s); // 이러면 union효과가 되나? 아님 Left join?? 확인필요
            OutputQueryResults(nameMatchQuery, String.Format("Concat based on partial name match \"{0}\":", nameMatch));

2.1.8. 여러 소스로 개체 컬렉션 채우기(두 파일읽어서 조인효과 구현)
- scores.csv
111, 97, 92, 81, 60
112, 75, 84, 91, 39
113, 88, 94, 65, 91
114, 97, 89, 85, 82
115, 35, 72, 91, 70
116, 99, 86, 90, 94
117, 93, 92, 80, 87
118, 92, 90, 83, 78
119, 68, 79, 88, 92
120, 99, 82, 81, 79
121, 96, 85, 91, 60
122, 94, 92, 91, 91
- names.csv
Omelchenko,Svetlana,111
O'Donnell,Claire,112
Mortensen,Sven,113
Garcia,Cesar,114
Garcia,Debra,115
Fakhouri,Fadi,116
Feng,Hanying,117
Garcia,Hugo,118
Tucker,Lance,119
Adams,Terry,120
Zabokritski,Eugene,121
Tucker,Michael,122
        // These data files are defined in How to: Join Content from Dissimilar Files (LINQ)
        string[] names = System.IO.File.ReadAllLines(@"../../../names.csv");
        string[] scores = System.IO.File.ReadAllLines(@"../../../scores.csv");

        // Merge the data sources using a named type.
        // var could be used instead of an explicit type.
        // Note the dynamic creation of a list of ints for the TestScores member. We skip 1 because the first string
        // in the array is the student ID, not an exam score.
        IEnumerable<Student> queryNamesScores =
            from name in names
            let x = name.Split(',')
            from score in scores
            let s = score.Split(',')
            where x[2] == s[0]  // score의 첫번째 컬럼과 names의 두번째 컬럼과 조인(학생ID)
            select new Student()  //이렇게 새로운 타입으로 new
            {
                FirstName = x[0],
                LastName = x[1],
                ID = Convert.ToInt32(x[2]), //타입명시 
                ExamScores = (from scoreAsText in s.Skip(1) //첫번째 값은 ID이므로 skip 
                              select Convert.ToInt32(scoreAsText)).
                              ToList()
            };

        // Optional. Store the newly created student objects in memory for faster access in future queries. Could be useful with
        // very large data files.
        List<Student> students = queryNamesScores.ToList();

        // Display the results and perform one further calculation.
        foreach (var student in students)
        {
            Console.WriteLine("The average score of {0} {1} is {2}.",
                student.FirstName, student.LastName, student.ExamScores.Average());
        }
2.1.9. 그룹을 사용하여 파일을 여러 파일로 분할
- names1.csv
Bankov, Peter
Holm, Michael
Garcia, Hugo
Potra, Cristina
Noriega, Fabricio
Aw, Kam Foo
Beebe, Ann
Toyoshima, Tim
Guy, Wey Yuan
Garcia, Debra
- names2.csv
Liu, Jinghao
Bankov, Peter
Holm, Michael
Garcia, Hugo
Beebe, Ann
Gilchrist, Beth
Myrcha, Jacek
Giakoumakis, Leo
McLin, Nkenge
El Yassir, Mehdi

        string[] fileA = System.IO.File.ReadAllLines(@"../../../names1.txt");
        string[] fileB = System.IO.File.ReadAllLines(@"../../../names2.txt");

        // Concatenate and remove duplicate names based on default string comparer
        var mergeQuery = fileA.Union(fileB);

        // Group the names by the first letter in the last name.
        var groupQuery = from name in mergeQuery
                         let n = name.Split(',')
                         group name by n[0][0] into g  //제일 첫글자 , n[0]은 첫번쨰 컬럼
                         orderby g.Key
                         select g;

        // Create a new file for each group that was created
        // Note that nested foreach loops are required to access
        // individual items with each group.
        foreach (var g in groupQuery)
        {
            // Create the new file name.
            string fileName = @"../../../testFile_" + g.Key + ".txt";
            // Write file. 그룹별로 파일생성
            using (System.IO.StreamWriter sw = new System.IO.StreamWriter(fileName))
            {
                foreach (var item in g)
                {
                    sw.WriteLine(item);
                    // Output to console for example purposes.
                    Console.WriteLine("   {0}", item);
                }
            }
        }

2.1.10. 서로 다른 파일의 콘텐츠 조인 
코드만 봐도 될듯
        string[] names = System.IO.File.ReadAllLines(@"../../../names.csv");
        string[] scores = System.IO.File.ReadAllLines(@"../../../scores.csv");
        // Name:    Last[0],                First[1],      ID[2],     Grade Level[3]
        //                  Omelchenko,    Svetlana,      11,          2
        // Score:   StudentID[0],  Exam1[1]   Exam2[2],  Exam3[3],  Exam4[4]
        //          111,           97,        92,        81,        60

        // This query joins two dissimilar spreadsheets based on common ID value.
        // Multiple from clauses are used instead of a join clause
        // in order to store results of id.Split.
        IEnumerable<string> scoreQuery1 =
            from name in names
            let nameFields = name.Split(',')
            from id in scores
            let scoreFields = id.Split(',')
            where nameFields[2] == scoreFields[0]
            select nameFields[0] + "," + scoreFields[1] + "," + scoreFields[2]
                   + "," + scoreFields[3] + "," + scoreFields[4];

        // Pass a query variable to a method and
        // execute it in the method. The query itself
        // is unchanged.
        OutputQueryResults(scoreQuery1, "Merge two spreadsheets:");

2.1.10. CSV 텍스트 파일의 열 값 계산 

         string[] lines = System.IO.File.ReadAllLines(@"../../../scores.csv");
          .....
        IEnumerable<IEnumerable<int>> query = from line in strs
            let x = line.Split(',')
            let y = x.Skip(1)
            select (
                            from str in y
                            select Convert.ToInt32(str));

        // Execute and cache the results for performance.
        // ToArray could also be used here.
        var results = query.ToList();

        // Find out how many columns we have.
        int columnCount = results[0].Count();

        // Perform aggregate calculations on each column.           
        // One loop for each score column in scores.
        // We can use a for loop because we have already executed the columnQuery in the call to ToList.
        for (int column = 0; column < columnCount; column++)
        {
            var res2 = from row in results
                       select row.ElementAt(column);

            double average = res2.Average(); //컬럼에 대한 average
            int max = res2.Max();
            int min = res2.Min();

            // 1 is added to column because Exam numbers
            // begin with 1
            Console.WriteLine("Exam #{0} Average: {1:##.##} High Score: {2} Low Score: {3}",
                          column + 1, average, max, min);
        }

2.2. LINQ 및 리플렉션
LINQ를 리플렉션과 함께 사용하여 지정한 검색 조건에 일치하는 메서드에 대한 특정 메타데이터를 검색할 수 있다

using System.Reflection;
using System.IO;
namespace LINQReflection
{
    class ReflectionHowTO
    {
        static void Main(string[] args)
        {
            Assembly assembly = Assembly.Load("System.Core, Version=3.5.0.0, Culture=neutral, PublicKeyToken= b77a5c561934e089");
            var pubTypesQuery = 
            from type in assembly.GetTypes() // 타입목록 추출
            where type.IsPublic
            from method in type.GetMethods() // 각 타입에서 메소드목록 추출
            where method.ReturnType.IsArray == true 
                      || ( method.ReturnType.GetInterface(typeof(System.Collections.Generic.IEnumerable<>).FullName ) != null
                     && method.ReturnType.FullName != "System.String" )
            group method.ToString() by type.ToString();
            // 이렇게 되면 타입콜렉션의 속성으로 method가 들어가는 형태임. 아래에서 키(콜렉션 내에서 타입을 식별하는)별로 메소드 출력
            foreach (var groupOfMethods in pubTypesQuery)
            {
                Console.WriteLine("Type: {0}", groupOfMethods.Key);
                foreach (var method in groupOfMethods)
                {
                    Console.WriteLine("  {0}", method);
                }
            }

            Console.WriteLine("Press any key to exit");
            Console.ReadKey();
        }
    } 
}

2.3. LINQ 및 파일 디렉터리
문자열을 다루면서 파일에 관련된 내용이 나오긴 했지만 여기에서 더 상세히 다룸.

2.1.1. 지정된 특성 또는 이름을 갖는 파일 쿼리 
//주어진 경로의 파일정보 가져오는 함수  
// This method assumes that the application has discovery  permissions for all folders under the specified path.
    static IEnumerable<System.IO.FileInfo> GetFiles(string path)
    {
        if (!System.IO.Directory.Exists(path))
            throw new System.IO.DirectoryNotFoundException();

        string[] fileNames = null;
        List<System.IO.FileInfo> files = new List<System.IO.FileInfo>();

        fileNames = System.IO.Directory.GetFiles(path, "*.*", System.IO.SearchOption.AllDirectories);           
        foreach (string name in fileNames)
        {
            files.Add(new System.IO.FileInfo(name));
        }
        return files;
    }
...
        string startFolder = @"c:\program files\Microsoft Visual Studio 9.0\";

        // Take a snapshot of the file system.
        IEnumerable<System.IO.FileInfo> fileList = GetFiles(startFolder);

        //Create the query
        IEnumerable<System.IO.FileInfo> fileQuery =
            from file in fileList
            where file.Extension == ".txt"
            orderby file.Name
            select file;

        //Execute the query. This might write out a lot of files!
        foreach (System.IO.FileInfo fi in fileQuery)         
             Console.WriteLine(fi.FullName);    // Create and execute a new query by using the previous
        // query as a starting point. fileQuery is not
        // executed again until the call to Last()
        var newestFile =
            (from file in fileQuery
            orderby file.CreationTime
            select new { file.FullName, file.CreationTime })
            .Last();

        Console.WriteLine("\r\nThe newest .txt file is {0}. Creation time: {1}",
            newestFile.FullName, newestFile.CreationTime);

2.1.2. 확장명에 따라 파일 그룹화 

        // Take a snapshot of the file system.
        string startFolder = @"c:\program files\Microsoft Visual Studio 9.0\Common7";

        // Used in WriteLine to trim output lines.
        int trimLength = startFolder.Length;

        // Take a snapshot of the file system.
        IEnumerable<System.IO.FileInfo> fileList = GetFiles(startFolder);

        // Create the query.
        var queryGroupByExt =
            from file in fileList
            group file by file.Extension.ToLower() into fileGroup
            orderby fileGroup.Key //그루핑이 되면서 동일그룹에 키값들이 할당됨에 유의
            select fileGroup;
      PageOutput(trimLength, queryGroupByExt); // 바로아래에 정의

   private static void PageOutput( int rootLength,
                                    IEnumerable<System.Linq.IGrouping<string, System.IO.FileInfo>> groupByExtList)
    {
        // Flag to break out of paging loop.
        bool goAgain = true;

        // "3" = 1 line for extension + 1 for "Press any key" + 1 for input cursor.
        int numLines = Console.WindowHeight - 3;

        // Iterate through the outer collection of groups.
        foreach (var filegroup in groupByExtList)
        {
            // Start a new extension at the top of a page.
            int currentLine = 0;

            // Output only as many lines of the current group as will fit in the window.
            do
            {
                Console.Clear();
                Console.WriteLine(filegroup.Key == String.Empty ? "[none]" : filegroup.Key);

                // Get 'numLines' number of items starting at number 'currentLine'.
                var resultPage = filegroup.Skip(currentLine).Take(numLines);

                //Execute the resultPage query
                foreach (var f in resultPage)
                {
                    Console.WriteLine("\t{0}", f.FullName.Substring(rootLength));
                }

                // Increment the line counter.
                currentLine += numLines;

                // Give the user a chance to escape.
                Console.WriteLine("Press any key to continue or the 'End' key to break...");
                ConsoleKey key = Console.ReadKey().Key;
                if (key == ConsoleKey.End)
                {
                    goAgain = false;
                    break;
                }
            } while (currentLine < filegroup.Count());

            if (goAgain == false)
                break;
        }
    }
2.1.3. 폴더 집합의 전체 바이트 수 쿼리
    static long GetFileLength(string filename)
    {
        long retval;
        try { System.IO.FileInfo fi = new System.IO.FileInfo(filename);
            retval = fi.Length;
        }
        catch (System.IO.FileNotFoundException)
        {
            // If a file is no longer present,
            // just add zero bytes to the total.
            retval = 0;
        }
        return retval;
    }
.....
        string startFolder = @"c:\program files\Microsoft Visual Studio 9.0\VC#";

        // Take a snapshot of the file system.
        // This method assumes that the application has discovery permissions
        // for all folders under the specified path.
        IEnumerable<string> fileList = System.IO.Directory.GetFiles(startFolder, "*.*", System.IO.SearchOption.AllDirectories);

        var fileQuery = from file in fileList
                        select GetFileLength(file);

        // Cache the results to avoid multiple trips to the file system.
        long[] fileLengths = fileQuery.ToArray();

        // Return the size of the largest file
        long largestFile = fileLengths.Max();

        // Return the total number of bytes in all the files under the specified folder.
        long totalBytes = fileLengths.Sum();

        Console.WriteLine("There are {0} bytes in {1} files under {2}",
            totalBytes, fileList.Count(), startFolder);
        Console.WriteLine("The largest files is {0} bytes.", largestFile);
2.1.3. 두 폴더의 내용 비교
세 가지 방법
- 두 개의 파일 목록이 동일한지 여부를 지정하는 부울 값 쿼리
- 양쪽 파일에 있는 파일을 검색하는 교차 부분 쿼리
- 한 폴더에는 있지만 다른 폴더에는 없는 파일을 검색하는 차집합 쿼리

    class FileCompare : System.Collections.Generic.IEqualityComparer<System.IO.FileInfo>
    {
        public FileCompare() { }

        public bool Equals(System.IO.FileInfo f1, System.IO.FileInfo f2)
        {
            return (f1.Name == f2.Name && f1.Length == f2.Length); //파일명과 길이비교
        }

        // Return a hash that reflects the comparison criteria. According to the
        // rules for IEqualityComparer<T>, if Equals is true, then the hash codes must
        // also be equal. Because equality as defined here is a simple value equality, not
        // reference identity, it is possible that two or more objects will produce the same
        // hash code.
        public int GetHashCode(System.IO.FileInfo fi)
        {
            string s = String.Format("{0}{1}", fi.Name, fi.Length);
            return s.GetHashCode();
        }
    }
라는 Compare클래스 미리정의 된 경우
            string pathA = @"C:\TestDir";
            string pathB = @"C:\TestDir2"; // Take a snapshot of the file system.
            IEnumerable<System.IO.FileInfo> list1 = GetFiles(pathA);
            IEnumerable<System.IO.FileInfo> list2 = GetFiles(pathB);

            //A custom file comparer defined below
            FileCompare myFileCompare = new FileCompare();

            // This query determines whether the two folders contain identical file lists, based on the custom file comparer
            // that is defined in the FileCompare class.
            // The query executes immediately because it returns a bool.
            bool areIdentical = list1.SequenceEqual(list2, myFileCompare);
            if (areIdentical == true)
                Console.WriteLine("the two folders are the same");
            else
                Console.WriteLine("The two folders are not the same");

            // Find the common files. It produces a sequence and doesn't execute until the foreach statement.
            var queryCommonFiles = list1.Intersect(list2, myFileCompare);
           if (queryCommonFiles.Count() > 0)
           {
                Console.WriteLine("The following files are in both folders:");
                foreach (var v in queryCommonFiles)
                {
                    Console.WriteLine(v.FullName); //shows which items end up in result list
                }
            }
            else
            {
                Console.WriteLine("There are no common files in the two folders.");
            }

            // Find the set difference between the two folders.
            // For this example we only check one way.
            var queryList1Only = (from file in list1 select file).Except(list2, myFileCompare);

            Console.WriteLine("The following files are in list1 but not list2:");
            foreach (var v in queryList1Only)
            {
                Console.WriteLine(v.FullName);
            }
2.1.4. 디렉터리 트리에서 가장 큰 파일을 하나 이상 쿼리 
파일 크기(바이트)와 관련된 5개의 쿼리방법
- 가장 큰 파일의 크기(바이트)를 검색하는 방법
- 가장 작은 파일의 크기(바이트)를 검색하는 방법
- 지정한 루트 폴더에 있는 하나 이상의 폴더에서 FileInfo 개체의 가장 큰 파일이나 가장 작은 파일을 검색하는 방법
- 가장 큰 10개 파일과 같은 시퀀스를 검색하는 방법
- 지정한 크기보다 작은 파일을 무시하여 파일 크기(바이트)에 따라 파일을 그룹으로 정렬하는 방법
       string startFolder = @"c:\program files\Microsoft Visual Studio 9.0\";

        // Take a snapshot of the file system.
        // fileList is an IEnumerable<System.IO.FileInfo>
        var fileList = GetFiles(startFolder);

        //Return the size of the largest file
        long maxSize =
            (from file in fileList
             let len = GetFileLength(file)
             select len).Max();

        Console.WriteLine("The length of the largest file under {0} is {1}",
            startFolder, maxSize);

        // Return the FileInfo object for the largest file by sorting and selecting from beginning of list
        System.IO.FileInfo longestFile =
            (from file in fileList
            let len = GetFileLength(file)
            where len > 0
            orderby len descending
            select file).First();

        Console.WriteLine("The largest file under {0} is {1} with a length of {2} bytes",
                            startFolder, longestFile.FullName, longestFile.Length);

        //Return the FileInfo of the smallest file
        System.IO.FileInfo smallestFile =
            (from file in fileList
            let len = GetFileLength(file)
            where len > 0
            orderby len ascending
            select file).First();

        Console.WriteLine("The smallest file under {0} is {1} with a length of {2} bytes",
                            startFolder, smallestFile.FullName, smallestFile.Length);

        //Return the FileInfos for the 10 largest files queryTenLargest is an IEnumerable<System.IO.FileInfo>
        var queryTenLargest =
            (from file in fileList
            let len = GetFileLength(file)
            orderby len descending
            select file).Take(10);

        Console.WriteLine("The 10 largest files under {0} are:", startFolder);

        foreach (var v in queryTenLargest)
        {
            Console.WriteLine("{0}: {1} bytes", v.FullName, v.Length);
        }


        // Group the files according to their size, leaving out files that are less than 200000 bytes.
        var querySizeGroups =
            from file in fileList
            let len = GetFileLength(file)
            where len > 0
            group file by (len / 100000) into fileGroup
            where fileGroup.Key >= 2
            orderby fileGroup.Key descending
            select fileGroup;


        foreach (var filegroup in querySizeGroups)
        {
            Console.WriteLine(filegroup.Key.ToString() + "00000");
            foreach (var item in filegroup)
            {
                Console.WriteLine("\t{0}: {1}", item.Name, item.Length);
            }
        }

2.1.5. 디렉터리 트리의 중복 파일 쿼리
 첫 번째 쿼리에서는 간단한 키를 사용하여 일치하는 항목을 확인(이름은 같지만 내용은 다를 수 있는 파일임)
 두 번째 쿼리에서는 복합 키를 사용하여 FileInfo 개체의 세 가지 속성에 대한 일치 여부를 확인
class QueryDuplicateFileNames
{
    static void Main(string[] args)
    {  
        // Uncomment QueryDuplicates2 to run that query.
        QueryDuplicates();           
        // QueryDuplicates2();

        // Keep the console window open in debug mode.
        Console.WriteLine("Press any key to exit.");
        Console.ReadKey();
    }

    static void QueryDuplicates()
    {
        // Change the root drive or folder if necessary
        string startFolder = @"c:\program files\Microsoft Visual Studio 9.0\";

        // Take a snapshot of the file system.
        IEnumerable<System.IO.FileInfo> fileList = GetFiles(startFolder);

        // used in WriteLine to keep the lines shorter
        int charsToSkip = startFolder.Length;

        // var can be used for convenience with groups.
        var queryDupNames =
            from file in fileList
            group file.FullName.Substring(charsToSkip) by file.Name into fileGroup
            where fileGroup.Count() > 1
            select fileGroup;

        // Pass the query to a method that will
        // output one page at a time.
        PageOutput<string,string>(queryDupNames);
    }

    // A Group key that can be passed to a separate method.
    // Override Equals and GetHashCode to define equality for the key.
    // Override ToString to provide a friendly name for Key.ToString()
    class PortableKey
    {
        public string Name { get; set; }
        public DateTime CreationTime { get; set; }
        public long Length {get;  set;}

        public override bool Equals(object obj)
        {
            PortableKey other = (PortableKey)obj;
            return other.CreationTime == this.CreationTime &&
                   other.Length == this.Length &&
                   other.Name == this.Name;
        }

        public override int GetHashCode()
        {
            string str = String.Format("{0}{1}{2}", this.CreationTime, this.Length, this.Name);
            return str.GetHashCode();
        }
        public override string ToString()
        {
            return String.Format("{0} {1} {2}", this.Name, this.Length, this.CreationTime);
        }
    }
    static void QueryDuplicates2()
    {
        // Change the root drive or folder if necessary.
        string startFolder = @"c:\program files\Microsoft Visual Studio 9.0\Common7";

        // Make the the lines shorter for the console display
        int charsToSkip = startFolder.Length;

        // Take a snapshot of the file system.
        IEnumerable<System.IO.FileInfo> fileList = GetFiles(startFolder);

        // Note the use of a compound key. Files that match
        // all three properties belong to the same group.
        // A named type is used to enable the query to be
        // passed to another method. Anonymous types can also be used
        // for composite keys but cannot be passed across method boundaries
        //
        var queryDupFiles =
            from file in fileList
            group file.FullName.Substring(charsToSkip) by
                new PortableKey{ Name=file.Name, CreationTime=file.CreationTime, Length=file.Length } into fileGroup
            where fileGroup.Count() > 1

            select fileGroup;

        var list = queryDupFiles.ToList();

        int i = queryDupFiles.Count();

        PageOutput<PortableKey, string>(queryDupFiles);              
    }


    // A generic method to page the output of the QueryDuplications methods
    // Here the type of the group must be specified explicitly. "var" cannot
    // be used in method signatures. This method does not display more than one
    // group per page.
    private static void PageOutput<K,V>(IEnumerable<System.Linq.IGrouping<K, V>> groupByExtList)
    {
        // Flag to break out of paging loop.
        bool goAgain = true;

        // "3" = 1 line for extension + 1 for "Press any key" + 1 for input cursor.
        int numLines = Console.WindowHeight - 3;

        // Iterate through the outer collection of groups.
        foreach (var filegroup in groupByExtList)
        {
            // Start a new extension at the top of a page.
            int currentLine = 0;

            // Output only as many lines of the current group as will fit in the window.
            do
            {
                Console.Clear();
                Console.WriteLine("Filename = {0}", filegroup.Key.ToString() == String.Empty ? "[none]" : filegroup.Key.ToString());

                // Get 'numLines' number of items starting at number 'currentLine'.
                var resultPage = filegroup.Skip(currentLine).Take(numLines);

                //Execute the resultPage query
                foreach (var fileName in resultPage)
                {
                    Console.WriteLine("\t{0}", fileName);
                }

                // Increment the line counter.
                currentLine += numLines;

                // Give the user a chance to escape.
                Console.WriteLine("Press any key to continue or the 'End' key to break...");
                ConsoleKey key = Console.ReadKey().Key;
                if (key == ConsoleKey.End)
                {
                    goAgain = false;
                    break;
                }
            } while (currentLine < filegroup.Count());

            if (goAgain == false)
                break;
        }
    }


    // This method assumes that the application has discovery
    // permissions for all folders under the specified path.
    static IEnumerable<System.IO.FileInfo> GetFiles(string path)
    {
        if (!System.IO.Directory.Exists(path))
            throw new System.IO.DirectoryNotFoundException();

        string[] fileNames = null;
        List<System.IO.FileInfo> files = new List<System.IO.FileInfo>();

        fileNames = System.IO.Directory.GetFiles(path, "*.*", System.IO.SearchOption.AllDirectories);
        foreach (string name in fileNames)
        {
            files.Add(new System.IO.FileInfo(name));
        }
        return files;
    }
}

2.1.6. 폴더의 파일 내용 쿼리  

        string startFolder = @"c:\program files\Microsoft Visual Studio 9.0\";

        // Take a snapshot of the file system.
        IEnumerable<System.IO.FileInfo> fileList = GetFiles(startFolder);

        string searchTerm = @"Visual Studio";

        // Search the contents of each file.
        // A regular expression created with the RegEx class
        // could be used instead of the Contains method.
        // queryMatchingFiles is an IEnumerable<string>.
        var queryMatchingFiles =
            from file in fileList
            where file.Extension == ".htm"
            let fileText = GetFileText(file.FullName)
            where fileText.Contains(searchTerm)
            select file.FullName;

        // Execute the query.
        Console.WriteLine("The term \"{0}\" was found in:", searchTerm);
        foreach (string filename in queryMatchingFiles)
        {
            Console.WriteLine(filename);
        }

3. LINQ to XML
http://msdn.microsoft.com/ko-kr/library/bb387098.aspx
DOM(문서 개체 모델) XML 프로그래밍 인터페이스와 유사.
LINQ to XML의 쿼리 기능은 기능 면에서(구문 면에서는 아니지만) XPath 및 XQuery와 유사.
클래스들

3.1. LINQ to XML과 DOM 비교
LINQ to XML과 현재 주로 사용되는 XML 프로그래밍 API인 W3C DOM(문서 개체 모델)의 몇 가지 주요 차이점

3.1.1. W3C DOM에서는 상향식으로 XML 트리를 빌드. 즉, 문서를 만들고 요소를 만든 다음 요소를 문서에 아래코드와 같이 추가 하는 형식이지만
LINQ to XML에서 함수 생성을 사용하여 훨씬 직관적인 코드를 생성할 수 있다.
 예) DOM의 Microsoft 구현인 XmlDocument를 사용하여 XML 트리를 만드는 일반적인 방법
XmlDocument doc = new XmlDocument();
XmlElement name = doc.CreateElement("Name");
name.InnerText = "Patrick Hines";
XmlElement phone1 = doc.CreateElement("Phone");
phone1.SetAttribute("Type", "Home");
phone1.InnerText = "206-555-0144";       
XmlElement phone2 = doc.CreateElement("Phone");
phone2.SetAttribute("Type", "Work");
phone2.InnerText = "425-555-0145";       
XmlElement street1 = doc.CreateElement("Street1");       
street1.InnerText = "123 Main St";
XmlElement city = doc.CreateElement("City");
city.InnerText = "Mercer Island";
XmlElement state = doc.CreateElement("State");
state.InnerText = "WA";
XmlElement postal = doc.CreateElement("Postal");
postal.InnerText = "68042";
XmlElement address = doc.CreateElement("Address");
address.AppendChild(street1);
address.AppendChild(city);
address.AppendChild(state);
address.AppendChild(postal);
XmlElement contact = doc.CreateElement("Contact");
contact.AppendChild(name);
contact.AppendChild(phone1);
contact.AppendChild(phone2);
contact.AppendChild(address);
XmlElement contacts = doc.CreateElement("Contacts");
contacts.AppendChild(contact);
doc.AppendChild(contacts);
이 코딩 스타일은 XML 트리의 구조에 대한 많은 정보를 시각적으로 제공하지 않는다.
LINQ to XML에서는 이러한 XML 트리 생성 방법을 지원하며 추가적으로 다음과 같이
함수 생성에서는 XElement 및 XAttribute 생성자를 사용하여 XML 트리를 빌드하는 방법을 제공

비교) LINQ to XML에서 함수 생성을 사용하여 동일한 XML 트리를 생성하는 방법
XElement contacts =
    new XElement("Contacts",
        new XElement("Contact",
            new XElement("Name", "Patrick Hines"),
            new XElement("Phone", "206-555-0144",
                new XAttribute("Type", "Home")),
            new XElement("phone", "425-555-0145",
                new XAttribute("Type", "Work")),
            new XElement("Address",
                new XElement("Street1", "123 Main St"),
                new XElement("City", "Mercer Island"),
                new XElement("State", "WA"),
                new XElement("Postal", "68042")
            )
        )
    );
3.1.2 LINQtoXML에서는  바로 위 코드와 같이  LINQ XML 요소(XElement)로 직접 XML핸들링이 가능하다.
XML 파일에서 T:System.Xml.Linq.XElement 개체를 직접 로드도 가능
T:System.Xml.Linq.XElement 개체를 파일이나 스트림으로 serialize가능


반면에 W3C DOM에서는 아래코드와 같이 XML 문서가 XML 트리의 논리 컨테이너로 사용
XmlDocument doc = new XmlDocument();
XmlElement name = doc.CreateElement("Name");
name.InnerText = "Patrick Hines";
doc.AppendChild(name);
여러 문서에서 요소를 사용하려면 여러 문서의 노드를 가져와야 하지만 LINQ to XML에서는 이러한 복잡한 작업이 필요 없다.
LINQ to XML을 사용할 때 문서의 루트 수준에서 주석이나 처리 명령을 추가하려는 경우에만 XDocument 클래스를 사용.

3.1.3 LINQ to XML에서는 이름 및 네임스페이스의 처리가 간단하다.
기존 DOM 프로그래밍에서는 이름, 네임스페이스 및 네임스페이스 접두사의 처리는 XML 프로그래밍의 복잡한 부분.
LINQ to XML에서는 네임스페이스 접두사를 처리하는 요구 사항을 제거하여 이름과 네임스페이스를 단순화.
네임스페이스 접두사를 제어할 수 있다.
네임스페이스 접두사를 명시적으로 제어하지 않을경우
LINQ to XML에서는 serialize할 때 네임스페이스 접두사를 할당하거나, 기본 네임스페이스를 사용하여 serialize해야한다.
기본 네임스페이스가 사용되는 경우 생성되는 문서에는 네임스페이스 접두사가 없다.
기존 DOM 프로그래밍 방식에서는 노드의 이름을 변경할 수 없다.
대신 새 노드를 만들고 모든 자식 노드를 새 노드에 복사해야 하므로 원래 노드 ID가 손실됩니다.
LINQ to XML에서는 노드에서 XName 속성을 설정할 수 있도록 하여 이 문제를 방지합니다.

3.1.3.1. XML 네임스페이스 작업(네임스페이스 접두사 제어)
 LINQ to XML에서 XML 이름을 나타내는 클래스는 XName
 - 네임스페이스를 사용하여 문서 만들기(C#)(LINQ to XML)

 예1) 네임스페이스가 하나 포함된 문서를생성. 기본적으로 LINQ to XML은 기본 네임스페이스를 사용하여 이 문서를 serialize
  // Create an XML tree in a namespace.
XNamespace aw = "http://www.adventure-works.com";
XElement root = new XElement(aw + "Root", new XElement(aw + "Child", "child content"));
Console.WriteLine(root);
결과)
<Root xmlns="http://www.adventure-works.com">
  <Child>child content</Child>
</Root>

예2) 네임스페이스 접두사가 포함된 네임스페이스를 선언
// Create an XML tree in a namespace, with a specified prefix
XNamespace aw = "http://www.adventure-works.com";
XElement root = new XElement(aw + "Root",
    new XAttribute(XNamespace.Xmlns + "aw", "http://www.adventure-works.com"),
    new XElement(aw + "Child", "child content")
);
Console.WriteLine(root);
결과)
<aw:Root xmlns:aw="http://www.adventure-works.com">
  <aw:Child>child content</aw:Child>
</aw:Root>

예3)두 네임스페이스가 포함된 문서를 만드는 방법
두 네임스페이스 중 하나는 기본 네임스페이스이고 다른 하나는 접두사가 포함된 네임스페이스
루트 요소에 네임스페이스 특성을 포함하면 http://www.adventure-works.com이 기본 네임스페이스가 되도록 네임스페이스가 serialize되고
www.fourthcoffee.com이 "fc" 접두사를 사용하여 serialize됩니다.
기본 네임스페이스를 선언하는 특성을 만들려면 네임스페이스 없이 이름이 "xmlns"인 특성을 만듭니다.
특성 값은 기본 네임스페이스 URI입니다.
// The http://www.adventure-works.com namespace is forced to be the default namespace.
XNamespace aw = "http://www.adventure-works.com";
XNamespace fc = "www.fourthcoffee.com";
XElement root = new XElement(aw + "Root",
    new XAttribute("xmlns", http://www.adventure-works.com), //기본 네임스페이스가 된다.
    new XAttribute(XNamespace.Xmlns + "fc", "www.fourthcoffee.com"),
    new XElement(fc + "Child",
        new XElement(aw + "DifferentChild", "other content")
    ),
    new XElement(aw + "Child2", "c2 content"),
    new XElement(fc + "Child3", "c3 content")
);
Console.WriteLine(root);
결과)
<Root xmlns="http://www.adventure-works.com" xmlns:fc="www.fourthcoffee.com">
  <fc:Child>
    <DifferentChild>other content</DifferentChild>
  </fc:Child>
  <Child2>c2 content</Child2>
  <fc:Child3>c3 content</fc:Child3>
</Root>

예4)네임스페이스 접두사가 있는 두 가지 네임스페이스가 포함된 문서를 만들기
XNamespace aw = "http://www.adventure-works.com";
XNamespace fc = "www.fourthcoffee.com";
XElement root = new XElement(aw + "Root",
    new XAttribute(XNamespace.Xmlns + "aw", aw.NamespaceName),
    new XAttribute(XNamespace.Xmlns + "fc", fc.NamespaceName),
    new XElement(fc + "Child",
        new XElement(aw + "DifferentChild", "other content")
    ),
    new XElement(aw + "Child2", "c2 content"),
    new XElement(fc + "Child3", "c3 content")
);
Console.WriteLine(root);
결과)
<aw:Root xmlns:aw="http://www.adventure-works.com" xmlns:fc="www.fourthcoffee.com">
  <fc:Child>
    <aw:DifferentChild>other content</aw:DifferentChild>
  </fc:Child>
  <aw:Child2>c2 content</aw:Child2>
  <fc:Child3>c3 content</fc:Child3>
</aw:Root>

예5)확장된 이름이 포함된 문자열을 전달하여 위와 동일한 결과얻기
{}안의 내용을 파싱해야하므로 성능이 명시적으로 XNamespace를 선언하는 경우보다 떨어진다.
// Create an XML tree in a namespace, with a specified prefix
XElement root = new XElement("{http://www.adventure-works.com}Root",
    new XAttribute(XNamespace.Xmlns + "aw", "http://www.adventure-works.com"),
    new XElement("{http://www.adventure-works.com}Child", "child content")
);
Console.WriteLine(root);
결과)
<aw:Root xmlns:aw="http://www.adventure-works.com">
  <aw:Child>child content</aw:Child>
</aw:Root>

3.1.3.2. C#에서 기본 네임스페이스 범위(LINQ to XML)
XML 트리에 나타나는 기본 네임스페이스는 쿼리의 범위에 포함되지 않는다.
기본 네임스페이스에 있는 XML을 사용하는 경우 XNamespace 변수를 선언하고 로컬 이름과 결합하여
쿼리에서 사용할 정규화된 이름을 만들어야만 쿼리식에서 사용할 수 있다.

예1)집합에서는 기본 네임스페이스의 XML이 로드되지만 적절하지 않게 쿼리되는 경우의 예
XElement root = XElement.Parse(
@"<Root xmlns='http://www.adventure-works.com'>
    <Child>1</Child>
    <Child>2</Child>
    <Child>3</Child>
    <AnotherChild>4</AnotherChild>
    <AnotherChild>5</AnotherChild>
    <AnotherChild>6</AnotherChild>
</Root>");
IEnumerable<XElement> c1 =
    from el in root.Elements("Child")
    select el;
Console.WriteLine("Result set follows:");
foreach (XElement el in c1)
    Console.WriteLine((int)el);
Console.WriteLine("End of result set");
결과)
Result set follows:
End of result set

예2) 집합에서는 네임스페이스의 XML을 쿼리할 수 있도록 필요한 수정을 하는 방법을
XElement root = XElement.Parse(
@"<Root xmlns='http://www.adventure-works.com'>
    <Child>1</Child>
    <Child>2</Child>
    <Child>3</Child>
    <AnotherChild>4</AnotherChild>
    <AnotherChild>5</AnotherChild>
    <AnotherChild>6</AnotherChild>
</Root>");
XNamespace aw = "http://www.adventure-works.com";
IEnumerable<XElement> c1 =
    from el in root.Elements(aw + "Child") //명시적으로 지정.
    select el;
Console.WriteLine("Result set follows:");
foreach (XElement el in c1)
    Console.WriteLine((int)el);
Console.WriteLine("End of result set");
결과)
Result set follows:
1
2
3
End of result set

3.1.4 XML을 로드하기 위한 정적 메서드 지원 
예) 파일에서 XML 로드
XElement booksFromFile = XElement.Load(@"books.xml");
Console.WriteLine(booksFromFile);

3.1.5 DTD 구문에 대한 지원 제거  
LINQ to XML에서는 엔터티와 엔터티 참조에 대한 지원 기능을 제거하여 XML 프로그래밍을 더욱 단순화.  

3.1.6 조각에 대한 지원   
LINQ to XML에서는 XmlDocumentFragment 클래스와 동일한 항목을 제공하지만 대부분의 경우에 XmlDocumentFragment 개념은 XNode의 IEnumerable<(Of <(T>)>)이나 XElement의 IEnumerable<(Of <(T>)>)로 형식화된 쿼리의 결과에 의해 처리될 수 있다.

3.1.7 XPathNavigator에 대한 지원  
LINQ to XML에서는 System.Xml.XPath 네임스페이스의 확장 메서드를 통해 XPathNavigator를 지원.
System.Xml.XPath..::.Extensions 클래스의 멤버함수를 이용

3.1.7.1 Extensions..::.CreateNavigator 메서드
XNode에 대해 XPathNavigator를 만듬.
이 메서드에서 반환되는 XPathNavigator를 사용하여 XML 트리를 편집할 수 없다. (CanEdit 속성은 false로 반환).
XDocumentType 노드에 대해 XPathNavigator를 만들 수 없다.(문서형식(XDocumentType)은 XPath 데이터 모델과 관련이 없다)
네임스페이스 선언은 왼쪽에서 오른쪽으로 보고.
XmlDocument 네임스페이스는 오른쪽에서 왼쪽으로 보고.
이 메서드에서 반환되는 탐색기에 대해 MoveToId 메서드는 지원되지 않음.
이 메서드를 사용하여 XSLT 변환가능.

 예)XML 트리를 만들고, XML 트리에서 XPathNavigator를 만들고, 새 문서를 만들고, 새 문서에 쓸 XmlWriter를 만든다.
그런 다음 XPathNavigator 및 XmlWriter를 변환에 전달하여 XSLT 변환을 호출할 수 있다.
변환이 완료된 후 새 XML 트리는 변환 결과로 채운다.

XSLT 변환을 수행하려면 XmlReader 또는 XPathNavigator를 사용.
XmlReader를 사용할 때 더 빨리 실행되는 변환도 있고, XPathNavigator를 사용할 때 더 빨리 실행되는 변환도 있습니다.

string xslMarkup = @"<?xml version='1.0'?>
<xsl:stylesheet xmlns:xsl='http://www.w3.org/1999/XSL/Transform' version='1.0'>
    <xsl:template match='/Parent'>
        <Root>
            <C1><xsl:value-of select='Child1'/></C1>
            <C2><xsl:value-of select='Child2'/></C2>
        </Root>
    </xsl:template>
</xsl:stylesheet>";

XDocument xmlTree = new XDocument(
    new XElement("Parent",
        new XElement("Child1", "Child1 data"),
        new XElement("Child2", "Child2 data")
    )
);

XDocument newTree = new XDocument();
using (XmlWriter writer = newTree.CreateWriter()) {
    // Load the style sheet.
    XslCompiledTransform xslt = new XslCompiledTransform();
    xslt.Load(XmlReader.Create(new StringReader(xslMarkup)));

    // Execute the transform and output the results to a writer.
    xslt.Transform(xmlTree.CreateNavigator(), writer);
}

Console.WriteLine(newTree);
결과)
<Root>
  <C1>Child1 data</C1>
  <C2>Child2 data</C2>
</Root>

3.1.7.2 Extensions..::.XPathEvaluate 메서드
XPath 식을 평가
예) 다음 예제에서는 특성이 있는 작은 XML 트리를 만든 다음 XPathEvaluate 메서드를 사용하여 특성을 검색합니다.
String xml = "<root a='value'/>";
XDocument d = XDocument.Parse(xml);
IEnumerable att = (IEnumerable)d.XPathEvaluate("/root/@a");
Console.WriteLine(att.Cast<XAttribute>().FirstOrDefault());
결과)
a="value"

3.1.7.3 Extensions..::.XPathSelectElement 메서드
XPath 식을 사용하여 XElement를 선택
예) 작은 XML 트리를 만든 다음 XPathSelectElement를 사용하여 단일 요소를 선택
XElement root = new XElement("Root",
    new XElement("Child1", 1),
    new XElement("Child2", 2),
    new XElement("Child3", 3),
    new XElement("Child4", 4),
    new XElement("Child5", 5),
    new XElement("Child6", 6)
);
XElement el = root.XPathSelectElement("./Child4");
Console.WriteLine(el);
결과)
<Child4>4</Child4>

3.1.7.4 Extensions..::.XPathSelectElements 메서드
XPath 식을 사용하여 요소 컬렉션을 선택
예) 작은 XML 트리를 만든 다음 XPathSelectElements를 사용하여 요소 집합을 선택합
XElement root = new XElement("Root",
    new XElement("Child1", 1),
    new XElement("Child1", 2),
    new XElement("Child1", 3),
    new XElement("Child2", 4),
    new XElement("Child2", 5),
    new XElement("Child2", 6)
);
IEnumerable<XElement> list = root.XPathSelectElements("./Child2");
foreach (XElement el in list)
    Console.WriteLine(el);
결과)
<Child2>4</Child2>
<Child2>5</Child2>
<Child2>6</Child2>

3.1.8 공백 및 들여쓰기에 대한 지원   
서식이 있는 XML을 serialize하는 경우 XML 트리의 유효 공백만 유지(LINQ to XML의 기본 동작)
LINQ to XML에서는 DOM의 경우처럼 serialize된 Whitespace 노드 형식을 사용하는 대신 XText 노드로 공백을 저장.

3.1.9 주석에 대한 지원    
LINQ to XML에서 주석을 사용하여 임의의 형식에 대한 임의의 개체를 XML 트리의 XML 구성 요소와 연결
주석은 XML infoset의 일부가 아니므로 serialize되거나 deserialize되지 않는다.
아래 메서드호출(형식별로 주석을 검색가능)
AddAnnotation - XObject의 주석 목록에 개체를 추가합니다.
Annotation - 지정된 형식의 첫 번째 주석 개체를 XObject에서 가져옵니다.
Annotations -  XObject에 대한 지정된 형식의 주석 컬렉션을 가져옵니다.
RemoveAnnotations - 지정된 형식의 주석을 XObject에서 제거합니다.

예1) XElement에 주석을 추가
 public class MyAnnotation {
    private string tag;
    public string Tag {get{return tag;} set{tag=value;}}
    public MyAnnotation(string tag) {
        this.tag = tag; }} public class Program {
    public static void Main(string[] args) {  
        MyAnnotation ma = new MyAnnotation("T1");
        XElement root = new XElement("Root", "content");
        root.AddAnnotation(ma);

        MyAnnotation ma2 = (MyAnnotation)root.Annotation<MyAnnotation>();
        Console.WriteLine(ma2.Tag);
    }
}
결과)
T1

예2)  요소의 주석을 검색.
public class MyAnnotation {
    private string tag;
    public string Tag {get{return tag;} set{tag=value;}}
    public MyAnnotation(string tag) {
        this.tag = tag; }} class Program {
    static void Main(string[] args) {  
        XElement root = new XElement("Root", "content");
        root.AddAnnotation(new MyAnnotation("T1"));
        root.AddAnnotation(new MyAnnotation("T2"));
        root.AddAnnotation("abc");
        root.AddAnnotation("def");

        IEnumerable<MyAnnotation> annotationList;
        annotationList = root.Annotations<MyAnnotation>();
        foreach (MyAnnotation ma in annotationList)
            Console.WriteLine(ma.Tag);
        Console.WriteLine("----");

        IEnumerable<string> stringAnnotationList;
        stringAnnotationList = root.Annotations<string>();
        foreach (string str in stringAnnotationList)
            Console.WriteLine(str);
    }
}
결과)
T1
T2
----
abc
def

3.1.10 스키마 정보에 대한 지원
LINQ to XML에서는 System.Xml.Schema 네임스페이스의 확장 메서드를 통해 XSD 유효성 검사를 지원
XML 트리가 XSD를 준수하는지 확인할 수 있으며, PSVI(Post-Schema-Validation Infoset)를 사용하여 XML 트리를 채울 수 있다
예1) XmlSchemaSet을 만든 다음 스키마 집합에 대해 두 XDocument 개체의 유효성을 검사.
string xsdMarkup =
    @"<xsd:schema xmlns:xsd='http://www.w3.org/2001/XMLSchema'>
       <xsd:element name='Root'>
        <xsd:complexType>
         <xsd:sequence>
          <xsd:element name='Child1' minOccurs='1' maxOccurs='1'/>
          <xsd:element name='Child2' minOccurs='1' maxOccurs='1'/>
         </xsd:sequence>
        </xsd:complexType>
       </xsd:element>
      </xsd:schema>";
XmlSchemaSet schemas = new XmlSchemaSet();
schemas.Add("", XmlReader.Create(new StringReader(xsdMarkup)));

XDocument doc1 = new XDocument(
    new XElement("Root",
        new XElement("Child1", "content1"),
        new XElement("Child2", "content1")
    )
);

XDocument doc2 = new XDocument(
    new XElement("Root",
        new XElement("Child1", "content1"),
        new XElement("Child3", "content1")
    )
);

Console.WriteLine("Validating doc1");
bool errors = false;
doc1.Validate(schemas, (o, e) =>
                     {
                         Console.WriteLine("{0}", e.Message);
                         errors = true;
                     });
Console.WriteLine("doc1 {0}", errors ? "did not validate" : "validated");

Console.WriteLine();
Console.WriteLine("Validating doc2");
errors = false;
doc2.Validate(schemas, (o, e) =>
                     {
                         Console.WriteLine("{0}", e.Message);
                         errors = true;
                     });
Console.WriteLine("doc2 {0}", errors ? "did not validate" : "validated");
결과)
Validating doc1
doc1 validated

Validating doc2
The element 'Root' has invalid child element 'Child3'. List of possible elements expected: 'Child2'.
doc2 did not validate

예2) :Track('ctl00_rs1_mainContentContainer_cpe497038_c|ctl00_rs1_mainContentContainer_ctl23',this);" href="http://msdn.microsoft.com/ko-kr/library/bb387025.aspx">샘플 XML 파일: 고객 및 주문(LINQ to XML)에 있는 XML 문서가 :Track('ctl00_rs1_mainContentContainer_cpe497038_c|ctl00_rs1_mainContentContainer_ctl24',this);" href="http://msdn.microsoft.com/ko-kr/library/bb675181.aspx">샘플 XSD 파일: 고객 및 주문에 있는 스키마별로 유효한지 확인한 다음 소스 XML 문서를 수정합니다. 여기에서는 첫 번째 고객에 대한 CustomerID 특성을 변경합니다. 변경한 후에는 주문이 존재하지 않는 고객을 참조하게 되므로 XML 문서가 더 이상 유효하지 않습니다.
XmlSchemaSet schemas = new XmlSchemaSet();
schemas.Add("", "CustomersOrders.xsd");

Console.WriteLine("Attempting to validate");
XDocument custOrdDoc = XDocument.Load("CustomersOrders.xml");
bool errors = false;
custOrdDoc.Validate(schemas, (o, e) =>
                     {
                         Console.WriteLine("{0}", e.Message);
                         errors = true;
                     });
Console.WriteLine("custOrdDoc {0}", errors ? "did not validate" : "validated");

Console.WriteLine();
// Modify the source document so that it will not validate.
custOrdDoc.Root.Element("Orders").Element("Order").Element("CustomerID").Value = "AAAAA";
Console.WriteLine("Attempting to validate after modification");
errors = false;
custOrdDoc.Validate(schemas, (o, e) =>
                     {
                         Console.WriteLine("{0}", e.Message);
                         errors = true;
                     });
Console.WriteLine("custOrdDoc {0}", errors ? "did not validate" : "validated");
결과)
Attempting to validate
custOrdDoc validated

Attempting to validate after modification
The key sequence 'AAAAA' in Keyref fails to refer to some key.
custOrdDoc did not validate


3.2. LINQ to XML과 다른 XML 기술 비교
LINQ to XML, XmlReader, XSLT, MSXML 및 XmlLite와 같은 XML 기술을비교

3.2.1 LINQ to XML 와 XmlReader
XmlReader는 캐시하지 않으며 정방향으로만 작동하는 빠른 파서.
LINQ to XML은 XmlReader를 기반으로 구현되었으며 두 기술은 밀접하게 통합되어 있다.( XmlReader를 단독으로 사용가능)

예)초당 수백 개의 XML 문서를 구문 분석할 웹 서비스를 빌드하는데 문서의 구조가 동일하여
XML의 구문을 분석하는 코드의 구현을 하나만 작성하면 되는 경우 XmlReader를 단독으로 사용할 수 있습니다.
반면에 크기가 작은 다양한 XML 문서의 구문을 분석하는 시스템을 빌드하는데 각 문서의 구조가 서로 다르면
LINQ to XML에서 제공하는 성능 향상 기능을 사용할 수 있습니다.

3.2.2 LINQ to XML 와 XSLT
LINQ to XML과 XSLT는 모두 광범위한 XML 문서 변환 기능을 제공
XSLT는 규칙 기반의 선언적 방법.
고급 XSLT 프로그래머는 상태 비저장 방법을 강조하는 함수형 프로그래밍 스타일로 XSLT를 작성.
이 경우 부작용 없이 구현되는 순수 함수를 사용하여 변환을 작성할 수 있다.
이 규칙 기반 방법 또는 함수 방법은 대부분의 개발자에게 익숙하지 않으며 배우는 데 많은 시간과 노력이 필요.

XSLT는 고성능 응용 프로그램을 생성하는 생산성이 매우 높은 시스템.
예를 들어 규모가 큰 일부 웹 회사에서는 다양한 데이터 저장소에서 가져온 XML에서 HTML을 생성하는 방법으로 XSLT를 사용.
관리되는 XSLT 엔진은 XSLT를 CLR 코드로 컴파일하며 일부 시나리오에서 네이티브 XSLT 엔진보다 성능이 훨씬 좋다.
그러나 XSLT에서는 개발자가 C# 및 Visual Basic 지식을 활용할 수 없으며 복잡하고 다른 프로그래밍 언어로 코드를 작성해야 합니다.
C#(또는 Visual Basic) 및 XSLT와 같은 통합되지 않은 두 가지 개발 시스템을 사용하면 소프트웨어 시스템을 개발하고 유지 관리하기가 더 어렵습니다.

LINQ to XML 쿼리 식을 완전히 익히고 나면 강력한 LINQ to XML 변환 기술을 쉽게 사용할 수 있다.
기본적으로 다양한 소스에서 데이터를 가져와서 XElement 개체를 동적으로 생성하고 전체 데이터를 새 XML 트리로 어셈블하는 함수 생성을 사용하여 XML 문서를 만든다.
변환을 통해 완전히 새로운 문서가 생성될 수 있다.
LINQ to XML에서는 비교적 쉽고 직관적으로 변환을 생성할 수 있으며 생성되는 코드도 쉽게 읽을 수 있다.
따라서 개발 및 유지 관리 비용이 줄어듭니다.

LINQ to XML은 XSLT를 대체하기 위한 것이 아니다.
XSLT는 여전히 복잡하고 문서 중심적인 XML(특히 문서 구조가 제대로 정의되지 않은 경우) 변환에 사용할 수 있다.

XSLT는 W3C(World Wide Web Consortium) 표준이므로 표준 기술만 사용해야 하는 요구 사항이 있는 경우 XSLT가 더 적합할 수 있다.
XSLT는 XML이므로 프로그래밍 방식으로 조작할 수 있다.

3.2.3 LINQ to XML과 MSXML 비교
MSXML은 Microsoft Windows에 포함된 XML을 처리할 수 있는 COM 기반 기술.
MSXML은 XPath 및 XSLT를 지원하는 DOM의 기본적인 구현을 제공하며 캐시하지 않는 이벤트 기반의 SAX2 파서도 포함하고 있다.
MSXML은 성능이 좋으며 대부분의 시나리오에서 기본적으로 안전.
AJAX 스타일의 응용 프로그램에서 클라이언트측 XML 처리를 수행하기 위해 Internet Explorer에서 MSXML에 액세스할 수 있다.
C++, JavaScript 및 Visual Basic 6.0을 비롯한 COM을 지원하는 모든 프로그래밍 언어에서 MSXML을 사용할 수 있다.
CLR(공용 언어 런타임) 기반의 관리 코드에서는 MSXML을 사용하지 않는 것이 좋다.

3.2.4 LINQ to XML과 XmlLite 비교
XmlLite는 캐시하지 않으며 정방향으로만 작동하는 가져오기 파서.
개발자는 주로 C++와 함께 XmlLite를 사용.
관리 코드와 함께 XmlLite를 사용하는 것은 권장되지 않는다.
XmlLite의 주요 이점은 대부분의 시나리오에서 안전하며 간단하고 빠른 XML 파서다.
 XmlLite에서 위협에 노출되는 영역은 매우 작다.
신뢰할 수 없는 문서의 구문을 분석해야 하고 서비스 거부나 데이터 노출과 같은 공격으로부터 보호하려면 XmlLite를 선택하는 것이 좋다.
XmlLite는 LINQ(통합 언어 쿼리)와 통합되지 않았으며 LINQ에 핵심적인 프로그래머 생산성 향상 기능을 제공하지 않는다.


// 프로그램을 구현 하면서 Object들을 하나의 구조에 모아서 사용해야 할 경우에 
// C# 에서는 List, ArrayList 등이 존재하는데 이들의 차이점이 무엇인지 알고 싶어졌다.
// 그래서 각각의 특성을 정리 해 보고자 한다.

1. What  are C# Collection Classes
    - The C# Collection classes are a set of classes designed specifically for grouping togather objects and performing tasks on them.
    - C# 의 컬렉션 클래스는 각 오브젝트들을 모으고 그 모아놓은 오브젝트들을 사용하기 위한 클래스들을 모아놓은 것이다.

2. Creating C# List Collection - List<T> and ArrayList
    - C#의 array와 비슷한 속성을 가지고 있으나 이 두 클래스들의 중요한 특징은 저장되어 있는 오브젝트들의 수에 따라 (자동적으로)커질수도 작아질 수도 있다는 것이다.(Array는 한번 생성하면 크기가 고정되어 있어 늘릴 수 없다.)
    - 저장되어 있는 namespace 도 다르다.(List<T>는 System.Collections.Generic ArrayList는 Sysyem.Collections)
    - 생성하는 방법은 간단히 new 함수를 사용하면 된다. 이때 다른점은 List는 가지고 있을 오브젝트의 type을 명시 해 주어야 한다는  것이다.

3. Other Caracteristics
    - 상황 : 일정 크기의 배열을 선언한 다음에 인덱스를 사용해서 오브젝트를 넣으려고 한다.
        - List<T> 와 ArrayList는 실패
        - 일반적인 Array 는 성공

4. 결론
    - 별다른 차이점이 없다.
    
Introduction

Text files are easy way to store data in either comma separated values or separated by new line. While working with many application usually we come across situations where we need to read and write data from / into text files. This article describes different approach of reading and writing data into the text files.

Essentials

In order to work with reading and writing data from / into text file, you will have to use System.IO namespace. You need to specify the path of the file to read from and write data into as Physical path on your hard driver. If you know the physical path of your file you want to read or write, you can specify as follows

string fileNameWithPath = @"E:\Csharp\temp\withsinglelinecode.txt";

If you do not know or not sure the path of the file from the drive or you want to create the text file in your applications sub folders, you can get the physical file path by using Server.MapPath method and passing the location of the file from your root directory of the application.

string fileNameWithPath = Server.MapPath("temp/withsinglelinecode1.txt");

In order to create a new line into the text file, you can use System.Environment.NewLine or "\r\n", to place a tab space you can use "\t". Reading data from text file and showing on the web page, will not automatically convert \r\n to new line, you will have to replace these characters with the <br /> as shown below.

litText.Text = contents.Replace("\r\n", "<br />");

Here litText is the Literal control I have placed in my .aspx page to show the contents of the file.

Reading and Writing with Single line of code


To simply write and read all contents of any any text file, you can use following one line code.

C# Code

 string data = "This is the first line . \r\n" 
    + "This is the second line. next two line space \r\n\r\n"
    + "\t This comes after a tab space";

// writing content into a text file with a single line of 
codeFile.WriteAllText(fileNameWithPath, data);

// reading content from a textfile with single line of codestring 
contents = File.ReadAllText(fileNameWithPath);

// Format the data with new line so that on the web page it appears in the new line
litText.Text = contents.Replace("\r\n", "< br />");

In the above code snippet, File.WriteAllText method accepts following parameters

  1. fileNameWithPath - physical location of the file with its name
  2. data - contents to write into the text file

File.ReadAllText method accepts following parameters

  1. fileNameWithPath - physical location of the file with its name

In the remaining lines, I have replaced the new line characters with the "<br />" so that it appears as next line in my web page.

Appending contents into a File and Reading data one by one

C# Code

 // Append data into the text file
using (StreamWriter writer = File.AppendText(fileNameWithPath))
{
    // writer.Write method will simply append the contents into the text // file without creating a new line
    writer.Write("NEW LINE ADDED without Creating a line line. ");
    writer.Write("This line will not appear into new line. ");
    
    // writer.WriteLine method append the contents into the text file 
    // and add a new line so that next appended contents appear in new line
    writer.WriteLine("Appended into above line but next appended contents will appear into a new line. ");
    writer.Write("This should appear in the next line. ");
}

// Read data from text file
using (StreamReader reader = File.OpenText(fileNameWithPath))
{
    // reads a single line of contents
    contents = reader.ReadLine();

    // reads complete contents of the the file
    contents += reader.ReadToEnd();
}

litText.Text = contents

In the above code snippets, I am using StreamWriter to append the contents into the file. File.AppendText method accepts physical location of the file with its name and returns StreamWriter, that can be used to append contents into the text file. This can be done using Write method or WriteLine method.

  1. writer.Write() - method accepts contents to append and simply append the contents at the end of the file.
  2. writer.WriteLine() - method accepts contents to append and add a new line after appending contents so that next appended contents appears in the new line.
Reading and Writing using FileStream

C# Code

// using File Stream
fileNameWithPath = Server.MapPath("temp/filestream.txt");

// writing contents using FileStream
using (FileStream stream = File.Open(fileNameWithPath, FileMode.OpenOrCreate))
{
     // writing data
     byte[] info = new UTF8Encoding(true).GetBytes(data);
     stream.Write(info, 0, info.Length);
}
// Reading contents using FileStream
using (FileStream stream = File.OpenRead(fileNameWithPath))
{
     // reading data
     StringBuilder strb = new StringBuilder();
     byte[] b = new byte[stream.Length];
     UTF8Encoding temp = new UTF8Encoding(true);

     while (stream.Read(b, 0, b.Length) > 0)
     {
           strb.Append(temp.GetString(b)); 
     }

     // write the contents of the file now
     Response.Write("Using FileStream 
" + strb.ToString() + "
"); }

FileStream object gives you extra control over how you want to read or write data from / into a file. This also let you specify the mode of the file to access like in Append mode, or Create mode or only Read mode. Also you can specify Type of access you want to have as third parameter of the File.Open() method.

FileStream buffers input and output for better performance. In order to use FileStream for reading and writing, you will need to use System.Text namespace as encoding related objects like UTF8Encoding contains in this namespace.

For detailed explanations FileStream, please visit MSDN.

'Framework' 카테고리의 다른 글

LINQ 자료  (2) 2009.10.09
C# Collection Classes (부제:List 와 ArrayList 의 차이 )  (0) 2009.10.06
[펌]Mircrosoft Office 2007 Groove 사용 방법  (0) 2009.09.02
XML 특수문자 처리  (0) 2009.08.11
.NET Naming Rule Guidelines  (0) 2009.07.02

Mircrosoft Office 2007 Groove란?

Groove는 현재의 인터넷에 가장 최적화된 업무공유 프로그램이 아닐까 싶습니다. 물론 다른 프로그램들도 많습니다만, 굳이 Groove를 최고의 프로그램으로 판단하는 것은 바로 "협업"에 이유가 있습니다.

협업이라.. 다소 생소하겠죠.. 우리가 이해하기 쉬운 말로 하면, 컨소시엄 혹은 프로젝트 팀 업무라고 할 수 있겠습니다.

즉, 하나의 프로젝트를 수행하는 수행원이 여러명일때, 그들의 각 업무를 어떻게 조화를 시켜야 할까요? 무조건 절차를 따지다 보면, 작업의 소요시간을 넘기는 경우가 빈번하게 발생됩니다. 그렇기에 프로젝트 매니저가 생겨나고, 그는 절차와 시간, 방법, 예상결과를 토데로 프로젝트를 수행해 나갑니다. 하지만, 각 수행원에 의해 실시간으로 수정 및 업데이트되는 데이터를 처리하기엔 너무나 힘이 듭니다.

이럴 때, 매니저는 Groove를 이용할 수 있습니다. 수행원간의 커뮤니케이션을 도와주고, 서로의 업무가 겹치지 않게 그리고, 많은 데이터들로 혼란스럽지 않게 도와 줍니다.

 

협업가능 프로그램

저는 평소업무에서도 컨소시엄 혹은 협업을 자주 하게 됩니다. 지금까지의 업무 공유는 메일이나, 메신져, 파일공유등을 이용해왔습니다. 프로그램의 선택방법은 우선 페이퍼와 같은 경우 이메일을 보내고, 파일용량이 크면 웹하드를 사용해야 합니다. 하지만 웹하드는 상대가 업로드를 해야 하고, 나는 업로드가 완료되었는지 확인 해야 하며 또한 다운로드를 해줘야 합니다. 그래서 메신저를 사용하곤 하는데, 채팅을 할 수 있다는 것은 좋지만, 파일전송은 오류가 많아서 별루 반갑지 않습니다. 또한 전송하는 파일을 어떠한 기준에 따라 정렬하기란 더욱 어렵구요.

 

Groove 프로그램 특징

그럴땐, Groove가 많은 부분들을 해결해 줍니다. 바로 다음과 같은 장점 때문입니다.

1. 고용량 파일

용량이 제법 큰 파일도 전송하는데에 무리는 없습니다. 물론 전송속도는 네트워크의 상태에 따라 다르겠지만, 파일 전송중에 끊기는 일은 타 프로그램에 비해 적습니다.

2. 실시간 파일 전송

파일이 전송되는 것을 누군가가 실행해야 하는 방식이 아닌, 프로그램이 알아서 업로드와 다운로드를 해줍니다.

3. 채팅

수행원간의 커뮤니티를 위해 채팅이 지원됩니다.

4. 웹 기능

게시판, 사진 게시판, 일정, 메모장등의 기능을 공유하여 사용할 수 있습니다.

5. 폴더 공유

수행원의 컴퓨터 간에 물리적으로 같은 네트워크에 있지 않아도 공유가 가능합니다.

6. 최신 파일 공유

하나의 파일을 여러명이 수정하고 수정후 공유하는 방식이기에, 항상 최신 파일을 보관, 배포 할 수 있습니다.

 

Groove 프로그램의 단점

1. 권한 설정

아직은 수행원에게 다양한 권한을 부여할 수는 없습니다. 현재는 3가지의 권한만 부여됩니다. 참석자, 관리자, 방문자입니다.

2. 초대방식

이메일을 이용하여 초대해야 하며, 피초대자가 능동적으로 검색하기가 힘들다는 것입니다.

3. History

수정과 업데이트로 인한 백업이 되지 않기에, 수해원 중 누군가가 실수를 범할 경우 곤란할 수 있습니다.

4. 어플리케이션

사용자와의 어플리케이션이 아직은 좀더 손봐야 하지 않을까 싶습니다. 게시판, 사진등.. 우리가 기존에 자주 사용하는 댓글과 본문에 사진 첨부등은 아직 좀 힘듭니다.

아쉬운 점은 많겠지만, 차차 낳아질 것이라고 기대해 봅니다.

 

Groove 설치

Groove는 현재 Ms Office중에 하나의 프로그램으로 되어 있습니다. 즉, Microsoft Office 2007을 설치하면 자동으로 추가 설치 할 수 있습니다. 하지만, office가 어려 버전이 있으므로, 확인을 해봐야 합니다.

위에서 보듯이, Groove는 Ultimate나 Enterprise로 설치해야만 합니다.

물론, 평가판을 이용할 순 있습니다. 60일 트라이얼이며, 설치해보진 않아서 확인해 보진 않았지만, 기능적 제한이 없으며, 60일 이후에도 계속 사용할 수 있다고 합니다.

다운로드는

http://trial.trymicrosoftoffice.com/trialkorea/product.aspx?sku=3082923&culture=ko-KR

에서 가능합니다.

언어 지원은

http://www.microsoft.com/downloads/details.aspx?familyid=a114d1c4-d42b-408a-b223-82a12e5e4a15&displaylang=ko

에서 다운 받으실 수 있습니다.

참고로, 제가 Ms Office 2003을 사용하면서 Groove 2007 평가판을 설치하려고 했으나, 원인 모를 오류로 설치가 되지 않았습니다.

평가판의 설치 혹은 Office 2007의 설치방법은 MS 웹사이트 혹은 매뉴얼을 참고하시길 바라며, 생략하겠습니다.

 

프로그램을 설치한 뒤 처음 실행을 하면 계정을 추가마법사가 실행됩니다.

새 계정 추가로 선택한뒤, 자신의 이메일 계정과 간단한 개인정보를 입력하시면 됩니다.

이때, 패스워드는 Groove를 실행할때마다 묻게 되는 비밀번호이니 꼭 기억해 두어야 합니다.

설치를 할 때 혹은 실행을 할 때 패스워드를 저장하기를 이용하면 되지만, 다른 PC에 계정 사용하기를 할 경우 다시 필요로 합니다.

 

Groove사용방법

작업영역 추가

작업영역추가 방법은 다양하며, 간단한 방법은 실행창의 바탕에 마우스 오른쪽의 클릭으로 손쉽게 생성 할 수 있습니다.

생성된 작업영역창은 아래와 같습니다.

 

구성원 추가

구성원을 추가하여 파일공유가 가능하도록 하는 방법을 알아 보겠습니다.

구성원 추가방법은 여러방법이 있습니다. 그중에서 역시나 가장 간편한 마우스 오른쪽기능을 사용해 보겠습니다.

실행창에서 해당 작업영역을 마우스 오른쪽으로 클릭-> 초대방법 -> Outlook를 선택합니다.

초대설정은 역할(권한)설정입니다. 관리자와 참석자 방문자에 따라 기능 추가/삭제 파일 쓰기권한등이 부여됩니다.

저는 참석자로 선택하여 확인을 클릭하겠습니다.

이제 이렇게 초대파일이 첨부되어 메일 창이 떴습니다. 받는 사람에 참석자의 이메일 주소를 입력한 뒤 보내기를 실행합니다.

상대는 이메일을 받고 첨부파일을 더블클릭하여 실행하면 Groove에서 해당 작업영역이 추가되고, 작업영역 다운로드(동기화)가 실행되며, 다운이 완료되면 구성원으로써 이용가능합니다.

 

파일전송에 의한 초대

이메일 계정이 없으시거나, 메신저와 같은 프로그램을 이용해서 초대파일을 보내고 싶으실때에는 이메일에 첨부된 파일과 같은 초대파일을 생성한뒤 구성원에게 보내 주시면 됩니다.

실행창의 해당 작업영역을 마우스 오른쪽으로 클릭 -> 초대를 파일로 저장을 선택합니다.

초대 파일 저장 창에서는 파일이 저장되는 위치를 확인해 두어야 합니다.

그리고, 역할도 설정을 해 두셔야 합니다.

 

이제 저장된 파일을 메신저와 같은 파일을 이용해서 구성원에게 전송하시면 됩니다.

해당 파일을 받은 구성원은 역시, 더블클릭으로 해당 파일을 실행하면 작업영역의 공유가 시작됩니다.

 

파일 공유방법

파일 공유 방법은 매우 쉽습니다.

작업영역창에서 파일 탭으로 이동한 뒤 '파일추가'를 실행하거나, 탐색기에서 파일을 작업영역창으로 드레그 하시면 복사가 됩니다.

다른 기능들은 이와 유사하거나, 게시판과 같은 어플리케이션이므로 생략합니다.

 

다른 컴퓨터에서 계정 사용하기

자신의 계정을 다른 컴퓨터에서 사용하는 기능이 있습니다. 즉, 회사에서 쓰는 작업영역 그대로 집에서 사용할 수 있습니다.

파일과 기타 기능들을 모두 똑 같은 환경에서 사용할 수 있습니다.

실행창의 파일 -> 다른 컴퓨터에서 계정 사용을 클릭합니다.

아래의 다른 컴퓨터에 복사할 계정 파일 저장을 클릭하신뒤 해당 파일을 다른컴퓨터로 이동시킨 후 실행하시면 됩니다.


특수문자 (Special Characters)
 
예약문자 | 그리스문자 | 기호 & 부호 | 화학기호 | 수학기호

--------------------------------------------------------------------------------

 
XML 예약문자
<, >, &는 XML tag 표시와 entity를 표시하는 XML 예약문자로, XML 문서에 그대로 사용할 수 없다.
 
< (less-than sign) &lt;
> (greater-than sign) &gt;
& (ampersand) &amp;


그리스문자
그리스 문자는 풀어서 사용한다.
 
α alpha
β beta
γ gamma
δ,Δ delta
ε epsilon
ζ zeta
η eta
θ theta
ι iota
κ kappa
λ lambda
μ micron
ν nu
ξ xi
ο omicron
π pi
ρ rho
σ, Σ sigma
τ tau
υ upsilon
φ phi
χ chi
ψ psi
ω, Ω omega


기호 & 부호
≤ &lt; or =
≥ &gt; or =
± +/-
˚ degrees
℃ degrees C
→ --&gt;
㎍, μG microgram
㎕, μL microliter
㎛, μM micrometer
® (R)
™ (TM)
χ2 chi─square


화학기호
화학기호는 윗첨자나 아랫첨자를 지정하지 않고 그대로 입력한다.
 
K+ K+
Cl- Cl-
Mg2+ Mg2+
CO2 CO2
H2O H2O


수학기호
수학기호는 윗첨자나 아랫첨자를 괄호 "( )" 안에 넣어서 입력한다.
 
102 10(2)
10-2 10(-2)
height2.239 height(2.239)


[.NET REFERENCE]
명명 규칙 (Naming Rule)
개발 업무시 각 개발 요소에 대한 이름으로 명명 규칙(Naming Rule)을 준수함으로써 코드의 완전성과 가독성을 향상시키고 명확한 의미 부여를 생산성을 높힐 수가 있습니다.

1. 명명 규칙 준수를 통한 업무 생산성 효과
   - 일종의 규약을 설정하여 프로젝트 개발자 및 구성원 들간의 원활한 업무적 의사소통 가능
   - 업무 단계별 산출물 사이의 일관성 유지
   - 명확한 의미 부여로 산출물 사이의 연관성 및 코드 가독성을 높힘

2. 명명 규칙 준수시 공통 사항
   - 업무 영역에 적합하고 연관되게 정의된 용어를 사용
   - 같은 유형의 요소에 대해서는 혼동을 방지하기 위해, 유사 명칭이나 대소문자만 다른 같은 명칭 사용 금지

3. 대소문자 혼용
   - 여러 개의 단어로 이루어진 이름의 경우 대소문자를 적절히 혼용하여 코드의 가독성을 향상시킵니다.
     (예시) MemberInfo, ProductDetail, FlightReservation
   - 대소문자 혼용 기법
유   형 설     명 예   시 적용 대상
Pascal Case 모든 단어의 첫번째 글자를 대문자로 쓴다. ProductCatalog 클래스
열거형(Enum), 열거값
이벤트
읽기전용 정적 필드
인터페이스
메서드
네임스페이스
속성
Camel Case 맨 첫번째 글자는 소문자로, 나머지 각 단어의 첫번째 글자를 소문자로 쓴다. productCatalog 매개변수(parameter)
인스턴스 필드, 클래스 변수
Uppercase 모든 글자를 대문자로 쓴다. System.IO 2글자 정도의 단어
상수


4. 약어 사용을 자제
   - 가능한 한 단어 전체를 사용 ==> 코드의 가독성 및 의미 파악 명확
      (예시) ProCat --> ProductCatalog
   -  명칭이 지나치게 길어지는 경우, 일반적으로 통용되거나 사전에 정의된 약어를 적절히 사용
      (예시) Temporary --> Temp,  Information --> Info,  UserInterface --> UI,  Number --> No
   - 약어 사용시, 2글자 단어는 Uppercase을, 2글자 초과시는 Pascal 또는 Camel case을 적용하여 사용
      (예시) System.Web.Ui --> System.Web.UI,  HTMLButton --> HtmlButton,  UserControlCheck --> UCCheck

5. 단어 사용시 주의사항
   - 업무에 적합하고 연관된 단어를 선정, 정의
   - 같은 유형시 유사한 명칭이나 대소문자만 서로 다르게 한 명칭 사용 금지
   - .NET Framework의 네임스페이스와 중복되지 않아야 함
   - .NET Framework 및 각 언어 (C#, VB.NET)에서 미리 정의된 키워드(keyword) 이름과 중복되지 않아야 함

6. 네임스페이스(Namespace) 명명 규칙
   - 네임스페이스 이름은 Pascal case을 적용
     (기본 구조) 회사명.도메인/부서.애플리케이션/시스템명.하위시스템.논리적계층
                 (예시) Microsoft.Windows.WindowsServer.IIS
   - 필요에 따라서는 업무 시스템에 맞게 '도메인/부서', '하위 시스템', '논리적 계층' 등의 부분 이름은 생략이 가능  
수   준 내   용 고정값 / 예제 생략가능
1 회사명 COMPANY X
2 도메인 / 부서 Development O
3 애플리케이션 / 시스템 이름 MessengerApp X
4 하위 시스템 InstantChatting O
5 논리적 계층 Biz O
      
   (예시) 회사명.부서.애플리케이션 --> COMPANY.Development.MessengerApp

7. 클래스(Class) 명명 규칙
   - 명사 / 명사구 사용
   - Pascal case 적용
   - C++ 에서의 MFC 스타일처럼 클래스를 나타내는 C 등의 접두사를 사용하지 않음
      (예시) CMessengerInfo --> MessengerInfo
   - 클래스의 성격에 따라 접두사 또는 접미사를 적절히 사용
   - 권장되는 클래스 명명 규칙
구  분 명명 표준 예  시
Base Class 공통기능명 + Base RuleBase, DacBase
Business Facade [Use Case Group명] + System/Facade/Service ProductSystem, OrderService
Business Rule 트랜잭션 사용 : [Use Case Group명] + _Tx EmployeeInfo_Tx
트랜잭션 미사용 : [Use Case Group명] + _NTx EmployeeInfo_NTx
Data Access [Table명] + _Dac Members_Dac
Type Dataset ds + [Table명] dsMembers
Exception Class [사용자정의예외명] + Exception BusinessRuleException


8. 인터페이스(Interface) 명명 규칙
   - 대문자 I 를 접두사로 사용하여 인터페이스임을 표시
      (예시) IServiceProvider, IComponent
   - 다른 내용은 클래스 명명 규칙과 동일

9. 메서드(Method) 명명 규칙
   -  동사 / 동사구(동사 + 명사) 사용
   - Pascal case 적용
   - Business Logic, Data Access 계층의 클래스 메서드는 사전에 약속된 동사 사용
      * Business Logic : 동사 + 명사 (조합)
      * Data Access : 동사
   - Business Logic과 Data Access 계층 클래스 메서드 명명 비교
작업구분 Business Facade / Rule Data Access
추가 Add / AddNew / Append / Register + 명사
   (예시) AddNewMemberInfo
Insert
수정 Modify / Change / Update + 명사
   (예시) UpdateMemberInfo
Update
삭제 Remove / Delete + 명사
   (예시) DeleteMemberInfo
Delete
조회 Get / Inquire + 명사
Get / Inquire + 명사 + By + 조건 (Optional)
   (예시) GetMemberInfo, GetMemberInfoByMemberID
Select / Inquire


10. 열거형(Enum) 명명 규칙
   - Pascal case 적용
   - 약어는 꼭 필요한 경우에만 사용
   - 열거형 type 이름에 Enum 접두사를 사용하지 않음
   - 열거형 type : 단수형 이름,  비트 필드 : 복수형 이름 사용
   - 비트 필드 열거형에는 항상 FlgAttribute를 추가

11. 변수(Variant) 및 필드(Field) 명명 규칙
   - 변수 이름은 짧으면서도 의미가 있는 단어 사용. 명사 / 명사구 사용
   - 헝가리안 표기법은 가급적 사용하지 않음
   - 루프 카운터(loop counter; 예 for문 등)를 제외하고는 모호한 이름(i, j, s, a 등)을 사용하지 않음
   - 메서드의 매개변수(parameter) : 매개변수의 이름과 type을 통해 매개변수의 의미를 확인할 수 있을 정도로 설명적이어야 함
   - 매개변수(parameter), 지역 변수, 인스턴스 필드(instance field) : Camel case 적용
   - 정적 필드(static field) : Pascal case 적용
   - 데이터 바인딩(data binding)시 데이터 바인딩 UI 컨트롤의 필드 이름과 데이터베이스의 필드 이름은 서로 동일하게 선언

12. 상수(Constants) 명명 규칙
   - Uppercase 적용 (모두 대문자 사용)
   - 단어와 단어 사이는 _ 로 연결
      (예시) int MAX_COUNT = 1000;

13. 속성(Property) 명명 규칙
   - 명사 / 명사구 사용
   - Pascal case 적용
   - 헝가리안 표기법을 사용하지 않음
   - 속성 : 내부 type (private)과 동일한 이름 사용 (단, VB.NET의 경우 내부 type 이름 앞에 m_ 접두사 사용, 예시 : m_Age)
   - Private 인스턴스 필드를 노출시, 속성명은 인스턴스 필드의 Camel case를 Pascal case로 전환
      (단, VB.NET의 경우 내부 type 이름 앞에 m_ 접두사 사용)
   (예시) C# 언어를 사용하여 속성 명명 규칙을 적용한 코드 예시

public class Hello {
  
// 내부 type인 경우, Camel case 적용
  
private int
itemCount;
  
  
// Private 인스턴스 필드를 외부에 노출하고자 하는 경우 또는 속성, Pascal case
  
public int
ItemCount {
      get { ... }
      set { ... }
   }
}



14. 이벤트(Event) 명명 규칙
   - Pascal case 적용
   - 헝가리안 표기법을 사용하지 않음
   - 이벤트 처리기 이름 : EventHandler 접미사 사용
      (예시) MouseEventHandler
   - 2개의 매개변수 지정 : sender, e
   - 이벤트 인수 클래스 이름 : EventArgs 접미사 사용
      (예시) MouseEventArgs
      (예시) public delegate void MouseEventHandler(object sender, MouseEventArgs e);
   - 시점을 나타내는 경우, BeforeXXX / AfterXXX 대신 ~ing / ~(e)d 형태를 사용
      (예시) BeforeClose, AfterClose  -->  Closing, Closed
정리 문서 

What is Cryptography?

암호화란 데이터를 보호 하는데 사용되며 그 외에도 유용하게 많은 곳에서 사용된다.
이는 데이터가 보여지고, 수정되어지는 것을 방지하고, 생성자의 데이터에 대한 무결성을 보장하기 위해 사용되어진다.
암호화는 데이터를 암호화 하여 암호화 된 상태에서 네트워크 상에 전송하며 그리고 수신측에서 다시 데이터를 암호 해독함으로써, 인터넷과 같은 안전하지 않은 네트워크 상에서 보안 통신을 제공하는 메커니즘을 제공한다.
Encryption은 데이터베이스에 저장된 암호와 같은 정보를 사람이 읽을수 있고 이해할 수 있게 하는 것을 방지하는 추가적인 보안 메커니즘에 이용되어 질수 있다.

Encryption Component
인크립션은 데이터를 암호화하고 해독하는 키와 조합된 암호화 알고리즘의 사용을 포함한다.
모든 암호화 알고리즘의 목적은 적절한 키 없이는 데이터를 암호해독 하는 것을 가급적 어렵게 만드는데 있다.
Microsoft .NET 프레임워크 클래스(System.Security.Cryptography)는 암호화에 대한 상세정보를 관리해 줄것이다.
이 클래스들은 동일한 인터페이스로 구현된다. ; 그래서 이 클래스들을 가지고 작업하는 것은 Cryptography 네임스페이스를 통해 하는 것과 동일하다.
프레임워크내의 어떤 클래스들은 단지 Microsoft CryptoAPI에 존재하는 알고리즘에 대한 래퍼이다. 다른 클래스들은 그 주요한 알고리즘에 대한 구현을 관리한다.

Public-Key Encryption
비대칭 암호화를 알려진 공개키 암호화는 공개키와 비밀키 쌍을 이용하여 데이터를 암호화하고 암호해독한다.
공개키는 누구나 사용할 수 있도록 되어있으며, 비밀키 소유자에게 보내질 데이터를 암호화 하는데 사용된다.
비밀키는 그 이름이 의미하듯 기밀을 유지한다. 비밀키는 데이터를 암호해독하는데 사용되며 오직 정확한 공개키가 데이터 암호화에 사용된 경우에만 작동하게 된다.
비밀키는 공개키로 암호화된 데이터를 암호해독 하는데 사용될수 있는 유일한 키이다.
키들은 여러 번 사용될수 있게 저장될수 있으며 또는 한번만 사용되기 위해 생성 될수도 있다.

비대칭 암호화 알고리즘은 대게 단지 적은 양의 데이터를 암호화하는데 효율적이다.
아래의 공개키 알고리즘은 .NET Framework 상에서 사용될 수 있다.

- Digital Signature Algorithm(DSA)
- RSA

Private-Key Encryption
대칭 암호화로 알려진 비밀키 암호화는 정보를 암호화하고 암호해독하는데 하나의 키를 이용한다. 키는 인증되지 않은 자에 의해 데이터가 암호해독되어 데이터가 손상되지 않도록 기밀을 유지해야 하며. 비밀키 알고리즘은 상대적으로 빠르며 거대한 데이터 스트림을 암호화하고 암호해독하는데 사용될수 있다.
비밀키 알고리즘은 block ciphers로 알려져 있다. 왜냐하면, 이 알고리즘은 데이터를 한번에 한 블록씩 암호화하기 때문이다.
Block ciphers는 알고리즘과 키에 기반하여 동일한 입력블럭을 동일한 출력블럭으로 암호화한다. 만일 데이터의 구조를 누군가 알고 있다면, 패턴이 인식되어 키가 역공학되어 질수 있는 가능성이 있다. 이를 해결하기 위해, .NET Framework 상의 클래스들은 Chaining이라 알려진 처리를 이용한다. 이는 이전 블록의 정보가 현재 블록을 암호화하는데 사용되도록 한다. 이는 키가 발견되지 않도록 방지한다. 이는 첫 블록의 데이터를 암호화 하는데 초기화 백터(Initialization vector : IV)를 요구한다.

아래의 비밀키 알고리즘은 .NET Framework 상에서 이용가능한 것이다. 각각의 상세내용에는 각 알고리즘에 대한 기본적인 정보를 담고 있으며 장, 단점을 포함하고 있다.

- Data Encryption Standard(DES) 알고리즘은 데이터를 64 bit 블록으로 암호화하고 해독하는데 64bit 키를 이용한다. 키가 64bit라 할지라도 효율적인 키 강도는 단지 56 bit이다. 적당한 시간안에 모든 DES 키를 검색할수 있는 하드웨어 장비가 존재한다. 이것은 DES 알고리즘이 깨어질수 있게 하였고, 그래서 이 알고리즘은 쓸모없는 것으로 취급되었다.
- RC2는 가변 키 길이 block cipher이다. 키 길이는 8bit에서 64bit로 가변적일 수 있다. 이는 특히 DES보다 안전한 대안으로서 설계되었다. 처리시간은 DES에 비해 2, 3배 빨라졌다. 하지만 .NET Framework 에서 사용가능한 RC2CryptoServiceProvider는 8 문자 또는 64bit 키로 제한되었다. 8문자 제한은 이 알고리즘이 DES가 가졌던 공격위험성을 그대로 가지게 하였다.
- TripleDES 알고리즘은 DES알고리즘의 세가지 계승적인 반복을 사용한다.
- 이 알고리즘은 2개 키, 3개 키를 사용한다. DES알고리즘과 같이 키 길이는 64bit 에 유효 키강도는 56bit이다. TripleDES 알고리즘은 DES 알고리즘의 결점을 정정하기 위해 설계되었다. 그러나 세 반복(Three Iteration)은 처리시간을 DES보다 세배 정보 느리게 하는 결과를 낳았다.
- Advanced Encryption Standard(AES) 알고리즘의 하나인 Rijndeal 알고리즘은 DES 알고리즘의 대안으로서 설계되었다. 키 강도는 DES보다 강하고 DES보다 나은 성능을 위해 설계되었다. 키의 길이는 128, 192, 256 bit로 가변적일수 있다. 이 알고리즘은 가장 신뢰할수 있으며 이번 칼럼 내용의 예제에서 사용하겠다.

Hashing Algorithm
Hashing은 특정 길이의 데이터를 고정 길이 바이트 시퀀스로 맵핑하는 것을 의미한다.
입력이 타이핑 테스트 이거나 법전의 내용이든 상관없이 결과는 동일 크기로 나타날 것이다.
해싱은 독특한 결과를 생산한다. 똑 같은 두개의 눈송이가 존재하지 않듯이 두개의 입력 조합이 동일한 해시 결과를 생산할 수는 없다. 입력이 하나의 문자가 다를지라도 다른 결과를 생산한다. .NET Framework는 아래의 해시 알고리즘에 대한 자원을 제공한다.

- HMACSHAI
- MACTripleDES
- MD5CryptoServiceProvider
- SHA1Managed
- SHA256Managed
- SHA384Managed
- SHA512Managed

How to Generate a Key and IV for Private-key Encryption
각 알고리즘은 사용되는 특정 키 사이즈를 가지고 있다. 각 키는 미리 정의된 크기, 전형적으로 1 문자(8bit) 에서 32문자(256bit)의 범위를 가지는, 에 맞추어야 한다.
어떤 알고리즘은 다양한 키 사이즈를 지원한다. 그러나 특정 알고리즘의 키 사이즈의 유효한 범위 내에 있어야만 한다. 우리는 128, 192, 256 bit key를 지원하는 RiJndeal를 사용할 것이다. 128 bit 키를 만드는 방법은 해싱 알고리즘의 하나를 이용해서 만든다.
아래의 code outline에서 클래스는 입력구를 받아서 키를 생성하고 IV를 생성하는 메서드를 포함한다.


// Initialize internal values
this._Key = new byte[24];
this._IV = new byte[16];

// Perform a hash operation using the phrase. This will generate
// a unique 48 character value to be used as the key and IV.
byte[] bytePhrase = Encoding.ASCII.GetBytes(Phrase);
SHA384Managed sha384 = new SHA384Managed();
sha384.ComputeHash(bytePhrase);
byte[] result = sha384.Hash;

// Transfer the first 24 characters of the hashed value to the key
// and 16 more characters for the initialization vector.
for( int loop=0; loop<24; loop++ ) this._Key[loop] = result[loop];
for( int loop=24; loop<40; loop++ ) this._IV[loop-24] = result[loop];


How to Use Private-Key Encryption
아래의 클래스는 데이터를 암호하 하는데 사용되는 비밀키를 생성하기 위해 해싱을 하는 방법을 데모한다. 암호해독 메서드는 암호화 함수에서 사용된 것의 역 함수를 사용함으로써 간단히 이용될수 있다.


using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;

internal class MyEncryptor
{
// Internal value of the phrase used to generate the secret key
private string _Phrase = "";
/// Set the phrase used to generate the secret key.
public override string Phrase
{
set
{
this._Phrase = value;
this.GenerateKey();
}
}

// Internal initialization vector value to
// encrypt/decrypt the first block
private byte[] _IV;

// Internal secret key value
private byte[] _Key;

///
/// Constructor
///

/// Secret phrase to
generate key
public MyEncryptor(string SecretPhrase)
{
this.Phrase = SecretPhrase;
}

///
/// Encrypt the given value with the Rijndael algorithm.
///

/// Value to encrypt
/// Encrypted value.
public string Encrypt(string EncryptValue)
{
CryptoStream encryptStream = null; // Stream used to encrypt
RijndaelManaged rijndael = null; // Rijndael provider
ICryptoTransform rijndaelEncrypt = null; // Encrypting object
MemoryStream memStream = new MemoryStream(); // Stream to contain
// data

try
{
if( EncryptValue.Length > 0 )
{
// Create the crypto objects
rijndael = new RijndaelManaged();
rijndael.Key = this._Key;
rijndael.IV = this._IV;
rijndaelEncrypt = rijndael.CreateEncryptor();
encryptStream = new CryptoStream(
memStream, rijndaelEncrypt, CryptoStreamMode.Write);

// Write the encrypted value into memory
byte[] input = Encoding.UTF8.GetBytes(EncryptValue);
encryptStream.Write(input, 0, input.Length);
encryptStream.FlushFinalBlock();

// Retrieve the encrypted value and return it
return( Convert.ToBase64String(memStream.ToArray()) );
}
else
{
return "";
}
}
finally
{
if( rijndael != null ) rijndael.Clear();
if( rijndaelEncrypt != null ) rijndaelEncrypt.Dispose();
if( memStream != null ) memStream.Close();
}
}

/*****************************************************************
* Generate an encryption key based on the given phrase. The
* phrase is hashed to create a unique 32 character (256-bit)
* value, of which 24 characters (192 bit) are used for the
* key and the remaining 8 are used for the initialization
* vector (IV).
*
* Parameters: SecretPhrase - phrase to generate the key and
* IV from.
*
* Return Val: None
***************************************************************/
private void GenerateKey(string SecretPhrase)
{
// Initialize internal values
this._Key = new byte[24];
this._IV = new byte[16];

// Perform a hash operation using the phrase. This will
// generate a unique 32 character value to be used as the key.
byte[] bytePhrase = Encoding.ASCII.GetBytes(SecretPhrase);
SHA384Managed sha384 = new SHA384Managed();
sha384.ComputeHash(bytePhrase);
byte[] result = sha384.Hash;

// Transfer the first 24 characters of the hashed value to the key
// and the remaining 8 characters to the intialization vector.
for( int loop=0; loop<24; loop++ ) this._Key[loop] = result[loop];
for( int loop=24; loop<40; loop++ ) this._IV[loop-24] = result[loop];
}
}


※ VS 2008 다운로드 

http://www.asp.net/downloads/essential/




※ ASP.NET AJAX

  •  Watch the ASP.NET AJAX Support in VS2008 Video
  •  Watch the Adding AJAX Functionality to an Existing ASP.NET Page Video
  •  Watch the Creating and Using an AJAX-enabled Web Service in a Web Site Video
  • The ASP.NET AJAX Site
  • ASP.NET AJAX Overview 




    ※ New ListView and DataPager Controls
  •  Watch the ListView Control Video
  •  Watch the DataPage Control Video
  • Using the ASP.NET 3.5 ListView Control
  • ListView Web Server Control 




    ※ LINQ and other .NET Framework 3.5 Improvements
  •  Watch the LINQ How Do I Video Series
  • Part 1: Introduction to LINQ and SQL
  • Part 2: Defining our Data Model Classes
  • Part 3: Querying our Database
  • Part 4: Updating our Database
  • Part 5: Binding UI using the ASP:LinqDataSource Control
  • Part 6: Retrieving Data Using Stored Procedures
  • Part 7: Updating our Database using Stored Procedures
  • Part 8: Executing Custom SQL Expressions
  • Part 9: Using a Custom LINQ Expression with the <asp:LinqDataSource> control
  • LinqDataSource Technology Overview 




    ※ LINQ and other .NET Framework 3.5 Improvements

    Using ASP.NET Web Services Roadmap 




    ※ New Web Design Interface
  •  Watch the New Designer Support in VS2008 Video
  •  Watch the Quick Tour of the VS2008 IDE Video
  •  Watch the VS2008 and Nested Masterpages Video
  •  Watch the Creating and Modifying a CSS File Video
  • VS 2008 Web Designer and CSS Support
  • VS 2008 Nested Master Page Support
  • Working with CSS Overview





    ※ JavaScript Debugging and Intellisense

  •  Watch the JavaScript Intellisense Support in VS2008 Video
  •  Watch the JavaScript Debugging in VS2008 Video
  •  Watch the IntelliSense for JScript and ASP.NET AJAX Video
  • Visual Studio 2008 JavaScript Debugging
  • Visual Studio 2008 JavaScript Intellisense 




    ※ Multi-targeting Support
  •  Watch the Multi-Targeting Support in VS2008 Video
  • Visual Studio 2008 Multi-Targeting Support
  • .NET Framework Multi-targeting Overview




    ※ Other Resources

  • ASP.NET AJAX Forums
  • Visual Studio 2008 Forum
  • Visual Web Developer 2008 Express Forum

  • 데이터베이스 데이터 테이블 출력하기

    HTML Table에 데이터베이스 레코드를 구성하는 2개의 Method를 보자

     

    Database 만들기

    프로젝트에 App_Data폴더 오른쪽 버튼을 클릭하여 추가에 새 항목을 선택하면 다음과 같은 화면이 나온다.

     

     데이터 -> SQL Server 데이터베이스 선택 후 이름은 Movies.mdf 로 한 후 추가 버튼을 클릭한다.

     

     

    Model Class 만들기

    Movies 데이터베이스 테이블의 레코드 구성을 보여주는 것부터 출발한다. Movies 데이터베이스 테이블은 다음과 같은 컬럼으로 구성되어 있다.

     

    Table : tblMovie

    Column Name

    Data Type

    Allow Nulls

    Id

    Int

    False

    Title

    Nvarchar(200)

    False

    Director

    NVarchar(50)

    False

    DateReleased

    DateTime

    False

     

    Movies 데이터베이스 테이블 나타내는 순서로는 LINQ to SQL 데이터 접근 기술을 사용할 것이다.

    다른 단어로는 MVC model classes by using LINQ to SQL

     

    프로젝트에 보면 Models폴더가 있는데 Models폴더 오른쪽 버튼을 클릭하여 추가에 새 항목을 선택하면 다음과 같은 화면이 나온다.

    데이터 -> LINQ to SQL 클래스 선택 후 이름은 Movie.dbml로 한 후 추가버튼을 클릭한다.

     

     

    서버탐색기에서 만든 테이블을 Movie.dbml에 끌어다 놓으면 다음과 같이 출력된다.

     (테이블의 컬럼은 알아서 만들어 보자 )

    여기까지 데이터베이스 생성 및 Models LINQ to SQL 파일 생성이 모두 완료되었다.

     

     

    Controller Action LINQ to SQL 사용하기

    Controller에서 LINQ to SQL을 사용하려면

    Using System.Linq; Using 프로젝트명.Models; 를 선언해야 한다.

     

    using System.Linq; // 추가

    using System.Web.Mvc;

    using MvcMyApplication3.Models; //추가

     

    namespace MvcMyApplication3.Controllers

    {

        [HandleError]

        public class HomeController : Controller

        {

            public ActionResult Index()

            {

                // 사용할 ModelsLINQ to SQL Class

                var dataContext = new MovieDataContext();  

                var movies = from m in dataContext.tblMovie // 테이블명

                             select m;

                return View(movies);

            }

        }

    }

     

    View에 데이터 출력하기

    <%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage" %>

    <%@ Import! Namespace="MvcMyApplication3.Models" %>

     

    <asp:Content ID="indexTitle" ContentPlaceHolderID="TitleContent" runat="server">

        Home Page

    </asp:Content>

     

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

     

    <table>

    <tr>

         <th>Id</th><th>Title</th><th>Release Date</th>

    </tr>

    <% foreach (tblMovie m in (IEnumerable)ViewData.Model)

    { %>

    <tr>

         <td><%= m.id %></td>

         <td><%= Html.Encode(m.Title) %></td>

         <td><%= m.DateReleased %></td>

    </tr>

    <% } %>

    </table>

     

    </asp:Content>

     

     여기까지 View page에 데이터 출력하는 것이 완료 되었다.

    [C# 코드]

    using System;
    using System.Web;
    using System.Web.Services;
    using System.Web.Services.Protocols;

    [WebService(Namespace =
    "http://tempuri.org/")]
    [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
    public class Service : System.Web.Services.WebService
    {
    public Service () {

    //Uncomment the following line if using designed components
    //InitializeComponent();
    }

    [WebMethod]
    public string HelloWorld(string strName) {
    return "Hello" +strName;
    }

    }


    [Html 코드]

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

    <html xmlns="http://www.w3.org/1999/xhtml">

    <head>

    <title>Untitled Page</title>

     <script type="text/javascript">

    function sendDataAsXML_SOAP() {

    var req_params = "", url = "", number = 0, type = "";

    /* Configure Parameters */

    url = "http://localhost/TestWebservice/Service.asmx";

    user = document.getElementById("Text1").value;

     req_params = "<?xml version=\"1.0\" encoding=\"utf-8\"?><soap:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\"><soap:Body><HelloWorld >";

    req_params = req_params + "<strName>" + user + "</strName>";

    req_params = req_params + "</HelloWorld></soap:Body></soap:Envelope>";

    alert(req_params);

    /* Send XML/SOAP Request To Web Service Using Browser's Javascript DOM */

    try {

    ajax_request = new XMLHttpRequest();

    }

    catch (trymicrosoft) {

    try {

    ajax_request = new ActiveXObject("Msxml2.XMLHTTP");

    }

    catch (othermicrosoft) {

    try {

    ajax_request = new ActiveXObject("Microsoft.XMLHTTP");

    }

    catch (failed) {

    ajax_request = false;

    }

    }

    }

    ajax_request.open("POST", url, true);

    ajax_request.setRequestHeader("Content-Type", "text/xml;charset=utf-8");

    ajax_request.onreadystatechange = receiveXML_SOAPData;

    ajax_request.send(req_params);

    }

    function receiveXML_SOAPData() {

    if (ajax_request.readyState == 4) {

    if (ajax_request.status == 200) {

    /* Parse The Response Data */

    //result.innerText = ajax_request.responseText;

    alert(ajax_request.responseText);

    }

    }

    }

    </script>

    </head>

    <body>

    <form action="#">

    <input type="text" id="Text1" />

    <input type="button" onclick="sendDataAsXML_SOAP();" value="Call WebService" />

    </form>

    </body>

    </html>

    Wcftestclient can be used to debug a WCF service. Here are two ways to enable wcftestclient debugging in Visual Studio VS2008 SP1 web applications.

    Tradition way:

    In Web application project property page, web tab, one can specify start external program and command line argument for IIS based WCF:

    1. Check Use Local IIS Web server and create a virtual directory on the local IIS (you've to enable WCF on .net3.0 with IIS7)

    2. Specify start external program: C:\Program Files\Microsoft Visual Studio 9.0\Common7\IDE\WcfTestClient.exe

    3. Specify command line argument: http://localhost/WcfService7/Service1.svc

    The problem with this approach is that if the project contains several wcf service or some other web page, ctrl-F5, F5 will still start the svc file.

    A hidden feature in VS2008 SP1:

    This is a hidden feature in VS2008 SP1, one can turn a project flag to enable wcftestclient.exe when debugging or run the svc file.

    For Web Application Project

    1. Make sure the Start action is "current page" instead of the "start external program". Servers can be either "Use Visual Studio Development Server" or "Use Local IIS Web server".

    2. Check the project directory, there is a file with .user extension, such as wcfservice7.csproj.user. Open it.

    3. This file is a xml file, near the end, there is a setting of:

    <EnableWcfTestClientForSVC>False</EnableWcfTestClientForSVC>

    4. Change the value to True

    5. Restart VS and reopen the project

    6. Focus to a svc file or its code behind file.

    7. Press Ctrl-F5 or F5

    The wcftestclient.exe will be automatically started for the current svc page.

    For website with wcf service

    This is a bit harder as the website does not have a project file. Do the following:

    1. Close the Visual studio, so websites.xml file get updated

    2. go to C:\Users\<username>\AppData\Local\Microsoft\WebsiteCache (This is Vista/Win2k8, XP/2003 is in a similar location)

    3. Open websites.xml using an editor:

    4. Find the line for the newly created website

    <Website RootUrl="D:\Shares\Programs\Test\WCFService3\" CacheFolder="WCFService3" addnewitemlang="Visual Basic" targetframework="3.5" vwdport="6442" _LastAccess="4/9/2009 3:57:04 PM"/>

    5. Add a new attribute enablewcftestclientforsvc ="True", so it looks like following:

    <Website RootUrl="D:\Shares\Programs\Test\WCFService3\" CacheFolder="WCFService3" addnewitemlang="Visual Basic" targetframework="3.5" enablewcftestclientforsvc="True" vwdport="6442" _LastAccess="4/9/2009 3:57:04 PM"/>

    6. Save the xml file

    7. Start VS, and reload the project

    8. Focus to svc file or its code behind file

    8. Press Ctrl-F5 or F5

    The wcftestclient.exe will be automatically started for the current svc page.

    Please note, this is only a workaround to help customers to launch wcftestclient for svc files more easily. We didn't make it true by default due to the limitation of the design. Hopefully, we can provide a better solution in the future.

    + Recent posts