2009-11-27 4 views
5

제 애플리케이션에서 다른 저장 프로 시저에서 반환 한 레코드 목록을 표시해야합니다. 각 상점 프로시 저는 여러 유형의 레코드를 리턴합니다 (즉, 컬럼 수와 컬럼 유형이 다름).C# 함수가 일반 객체/엔티티를 반환합니다.

내 원래의 생각은 각 레코드 유형에 대한 클래스를 만들고 해당 저장 프로 시저를 실행하고 List < MyCustomClass>를 반환하는 함수를 만드는 것이 었습니다. 이런 식으로 뭔가 :

public class MyCustomClass1 
    { 
     public int Col1 { get; set; } //In reality the columns are NOT called Col1 and Col1 but have proper names 
     public int Col2 { get; set; } 
    } 

    public static List<MyCustomClass1> GetDataForReport1(int Param1) 
    { 

     List<MyCustomClass1> output = new List<MyCustomClass1>(); 

     using (SqlConnection cn = new SqlConnection("MyConnectionString")) 
     using (SqlCommand cmd = new SqlCommand("MyProcNameForReport1", cn)) 
     { 
      cmd.CommandType = CommandType.StoredProcedure; 
      cmd.Parameters.Add("@Param1", SqlDbType.Int).Value = Param1; 

      SqlDataReader rdr=cmd.ExecuteReader(); 

      int Col1_Ordinal = rdr.GetOrdinal("Col1"); 
      int Col2_Ordinal = rdr.GetOrdinal("Col2"); 

      while (rdr.Read()) 
      { 
         output.Add(new MyCustomClass1 
         { 
          Col1 = rdr.GetSqlInt32(Col1_Ordinal).Value, 
          Col2 = rdr.GetSqlInt32(Col2_Ordinal).Value 
         }); 
      } 
      rdr.Close(); 
      } 

     return output; 

    } 

이 잘 작동하지만 난 내 클라이언트 코드에서이 기록을 조작 할 필요가 없기 때문에 (난 그냥 내 응용 프로그램 계층에서 그래픽 컨트롤에 바인딩 할 필요가) 정말하지 않습니다 내가 실제로 사용하지 않을 커스텀 클래스를 많이 사용하게되면이 방법으로 그렇게하는 것이 합리적이다.

public static DataTable GetDataForReport1(int Param1) 
    { 

     DataTable output = new DataTable(); 

     using (SqlConnection cn = new SqlConnection("MyConnectionString")) 
     using (SqlCommand cmd = new SqlCommand("MyProcNameForReport1", cn)) 
     { 
      cmd.CommandType = CommandType.StoredProcedure; 
      cmd.Parameters.Add("@Param1", SqlDbType.Int).Value = Param1; 

      output.Load(cmd.ExecuteReader()); 
     } 

     return output; 

    } 

이것은 내가 내 응용 프로그램 계층에서 사용하는 어떤 컨트롤에 바인딩 할 수있는 DataTable을 반환 :이 수행하는 트릭을 발견했다. DataTable을 사용하는 것이 정말로 필요한지 궁금합니다.

public static List<object> GetDataForReport1(int Param1) 
    { 

     List<object> output = new List<object>(); 

     using (SqlConnection cn = new SqlConnection("MyConnectionString")) 
     using (SqlCommand cmd = new SqlCommand("MyProcNameForReport1", cn)) 
     { 
      cmd.CommandType = CommandType.StoredProcedure; 
      cmd.Parameters.Add("@Param1", SqlDbType.Int).Value = Param1; 

      SqlDataReader rdr=cmd.ExecuteReader(); 

      int Col1_Ordinal = rdr.GetOrdinal("Col1"); 
      int Col2_Ordinal = rdr.GetOrdinal("Col2"); 

      while (rdr.Read()) 
      { 
         output.Add(new 
         { 
          Col1 = rdr.GetSqlInt32(Col1_Ordinal).Value, 
          Col2 = rdr.GetSqlInt32(Col2_Ordinal).Value 
         }); 
      } 
      rdr.Close(); 
      } 

     return output; 

    } 

다른 아이디어 :

나는 개체의 목록이 익명 클래스를 사용하여 만든 반환 할 수 없습니다? 기본적으로 나는 함수가 그래픽 컨트롤에 바인딩 할 수있는 '무언가'를 반환하기를 원하며 사용자 정의 클래스를 만들 필요가 없으므로 실제로 사용하지 않을 것입니다. 최선의 접근 방법은 무엇입니까?

+1

btw, SqlDataReader의 가장 좋은 방법은 다음 스타일입니다. using (var rdr = cmd.ExecuteReader() {while (rdr.Read() {..}} - 자동으로 닫힙니다. – abatishchev

+0

잘 찾아 냈습니다 ... – Anthony

답변

4

좋은 소식은 다음과 같습니다. entities vs datasets 문제가 처음 발생한 것은 아닙니다. 그것은 내 자신의 프로그래밍 경험보다 훨씬 오래된 토론입니다. DTO 또는 사용자 지정 엔터티를 쓰지 않으려면 옵션이 DataTables/DataSets이거나이 휠을 다시 만들 수 있습니다. 당신은 처음이 아니며 마지막이되지 않을 것입니다. DTO/Entities의 인수는 DataSets에서 얻는 성능 오버 헤드가 없다는 것입니다. DataSets는 다양한 열의 데이터 유형에 대한 많은 추가 정보를 저장해야합니다.

사용자 정의 엔티티 경로를 이동하면 개체에 대해 행복합니다. 속성 이름을 sprocs에서 반환 된 열 이름과 일치 시키려면 GetOrdinal을 사용하여 열을 속성에 매핑하는 모든 GetDataForReport() 매핑 함수를 작성하지 않아도됩니다. 운좋게도 일부 스마트 원숭이는 그 문제를 적절히 말했습니다. here.

편집 : 나는 (실버 DataGrids를에 데이터 세트를 바인딩)는 오늘 완전히 다른 문제를 연구하고 동적으로 생성 된 객체 (IL 사용)의를 IEnumerable IDictionary의 IEnumerable을 변환하는 방법을 보여줍니다 블라디미르 Bodurov에 의해 this article 건너 온되었다. 동적 컬렉션 문제를 해결하기 위해 IDictionary의 IEnumerable 대신 데이터 렌더링을 허용하도록 확장 메서드를 쉽게 수정할 수 있다고 생각했습니다. 그것은 꽤 멋지다. 데이터 집합이나 사용자 지정 엔터티가 더 이상 필요하지 않다는 점에서 정확히 수행 할 것이라고 생각합니다.사실상 사용자 정의 엔티티 컬렉션으로 끝나지 만 실제 클래스를 작성하는 오버 헤드를 잃게됩니다. 당신이 게으른 경우

가 여기 블라디미르의 사전 수집에 DataReader를을 켤 수있는 방법이다 (실제로는 자신의 확장 방법을 변환보다 효율적) : 익명 클래스를 사용하는 경우, 당신은 여전히 ​​각을 정의

public static IEnumerable<IDictionary> ToEnumerableDictionary(this IDataReader dataReader) 
{ 
    var list = new List<Dictionary<string, object>>(); 
    Dictionary<int, string> keys = null; 
    while (dataReader.Read()) 
    { 
     if(keys == null) 
     { 
      keys = new Dictionary<int, string>(); 
      for (var i = 0; i < dataReader.FieldCount; i++) 
       keys.Add(i, dataReader.GetName(i)); 
     } 
     var dictionary = keys.ToDictionary(ordinalKey => ordinalKey.Value, ordinalKey => dataReader[ordinalKey.Key]); 
     list.Add(dictionary); 
    } 
    return list.ToArray(); 
} 
+0

당신이 의미하는 바를 알고 있지만 내 질문은 중간에 있다고 생각합니다. 단지 '사용자 지정 엔티티 또는 데이터 집합의 목록을 반환해야합니까?'뿐만 아니라 일반적인 개체 목록을 반환 할 수 있다고 언급했습니다. List < object >) 이렇게하면 사용자 지정 엔티티 목록이 아니지만 데이터 집합이 아닙니다. – Anthony

+0

.net 4를 사용하는 경우 목록 을 사용할 수 있습니다. e 귀하의 역 동성은 실제로 Expando 객체입니다 (http://bit.ly/TDzbN). – grenade

+0

기사 링크를 가져 주셔서 감사합니다. 정확히 필요한 것 같아서 실제로 엔티티 또는 데이터 세트가 아닌 솔루션을 제공합니다. 불행히도 .net 4를 사용하지는 않지만이 마음을 지킬 것입니다. – Anthony

1

XmlSerialize하고 효율성을 높이려면 익명으로 지정할 수 없습니다. 변경 사항을 다시 보내시겠습니까? 익명의 클래스는 그다지 유용하지 않을 것입니다.

데이터를 격자 화하는 것 이외의 다른 작업을 수행하지 않으려는 경우 DataTable이 사용자 환경에 적합한 답변 일 수 있습니다.

일반적으로 관심있는 비즈니스 로그인이있는 경우 그리드를 둘러 보는 대신 데이터 전송 객체 등을 사용해야합니다.

+0

데이터가 다시 바뀌지 않을 것입니다 데이터 전송 객체를 사용하면 맞춤 클래스를 사용하는 것과 같지 않은가요? 각 레코드 유형에 대해 DTO를 만들어야합니다 . – Anthony

0

유형. 유형 추론을 사용하여 입력량을 줄이면 클래스 유형에 따라 네임 스페이스가 복잡해지지 않습니다.

여기에서 사용하는 단점은 메서드에서 반환하려는 것입니다. 지적하신대로, 이것을 수행하는 유일한 방법은 오브젝트에 캐스트하는 것입니다. 그러면 모든 데이터가 리플렉션없이 액세스 할 수 없게됩니다! 이 데이터로 어떤 프로세싱이나 계산을하지 않을 것입니까? 값 대신 그림 중 하나를 그림으로 표시하거나 일부 조건에 따라 숨 깁니 까? 단 하나의 조건문 만 가질지라도 익명 클래스 버전으로 처리하면 원하지 않게됩니다.

관련 문제