2014-01-21 2 views
6

데이터베이스로 쿼리하여 데이터를 가져옵니다. 1 행 이상일 수 있습니다. IEnumerable에 저장합니다.IEnumerable <dynamic>을 DataTable로 변환합니다.

왜 동적입니까? 테이블에 새로운 열을 추가 할 수 있기 때문에 코드를 변경하여 다시 적용하려고하지 않습니다.

그런 다음 IEnumerable을 datatable로 변환합니다. 동적 객체 내부의 속성을 가져 오는 데 문제가 있습니다. 누구든지 나를 도울 수 있습니까? 당신이 클래스 DataRecordDynamicWrapper에 선언 된 공공 재산이 없기 때문에 a_oProperties가 비어

DataTable dt; 
string query = "SELECT * FROM WORKSHOP WHERE WORKSHOPID = 1"; 

// Execute Query 
var result = Execute(query); 

// Convert IEnumerable<dynamic> to DataTable (I Have Problem Here) 
dt = CsvConverter.EnumToDataTable(result); 

// Convert DataTable To CSV 
var csv = CsvConverter.DataTableToCsv(dt, ",", true); 

// Save File 
string fileName = Path.GetTempPath() + Guid.NewGuid().ToString() + ".csv"; 
File.AppendAllText(fileName, csv); 

// Method to Execute Query 
public IEnumerable<dynamic> Execute(string commandText) 
{ 
    using (var result = databaseManager.ReadData(commandText, false)) 
     foreach (IDataRecord record in result) 
     { 
     yield return new DataRecordDynamicWrapper(record); 
     } 
} 

// Wrapper of Dynamic Record 
public class DataRecordDynamicWrapper : DynamicObject 
{ 
    private IDataRecord _dataRecord; 
    public DataRecordDynamicWrapper(IDataRecord dataRecord) { _dataRecord = dataRecord; } 

    public override bool TryGetMember(GetMemberBinder binder, out object result) 
    { 
     result = _dataRecord[binder.Name]; 
     return result != null; 
    } 
} 

// Method to Convert Enum to DT 
public static DataTable EnumToDataTable<T>(IEnumerable<T> l_oItems) 
    { 
     DataTable oReturn = new DataTable(typeof (T).Name); 
     object[] a_oValues; 
     int i; 

     //#### Collect the a_oProperties for the passed T 
     PropertyInfo[] a_oProperties = typeof (T).GetType().GetProperties(); 


     //#### Traverse each oProperty, .Add'ing each .Name/.BaseType into our oReturn value 
     //####  NOTE: The call to .BaseType is required as DataTables/DataSets do not support nullable types, so it's non-nullable counterpart Type is required in the .Column definition 
     foreach (PropertyInfo oProperty in a_oProperties) 
     { 
      oReturn.Columns.Add(oProperty.Name, BaseType(oProperty.PropertyType)); 
     } 

     //#### Traverse the l_oItems 
     foreach (T oItem in l_oItems) 
     { 
      //#### Collect the a_oValues for this loop 
      a_oValues = new object[a_oProperties.Length]; 

      //#### Traverse the a_oProperties, populating each a_oValues as we go 
      for (i = 0; i < a_oProperties.Length; i++) 
      { 
       a_oValues[i] = a_oProperties[i].GetValue(oItem, null); 
      } 

      //#### .Add the .Row that represents the current a_oValues into our oReturn value 
      oReturn.Rows.Add(a_oValues); 
     } 

     //#### Return the above determined oReturn value to the caller 
     return oReturn; 
    } 

    public static Type BaseType(Type oType) 
    { 
     //#### If the passed oType is valid, .IsValueType and is logicially nullable, .Get(its)UnderlyingType 
     if (oType != null && oType.IsValueType && 
      oType.IsGenericType && oType.GetGenericTypeDefinition() == typeof (Nullable<>) 
      ) 
     { 
      return Nullable.GetUnderlyingType(oType); 
     } 
      //#### Else the passed oType was null or was not logicially nullable, so simply return the passed oType 
     else 
     { 
      return oType; 
     } 
    } 
+0

어떤 문제가 있습니까? 배열'a_oProperties'가 비어 있습니까? –

+0

@AlbertoSolano 예, 그것은 비어 있습니다 ...하지만 l_oItems에 뭔가가 있습니다. –

답변

3

리플렉션 API를 사용하여 DynamicObject의 동적으로 바인딩 된 멤버를 열거 할 수 없습니다. 필요에 따라 이름으로 바인딩 할 수 있습니다. 작성된 코드는 실제로 속성이 정의되지 않은 실제 DynamicObject 클래스에 정의 된 속성 만 반환합니다 (따라서 빈 배열).

public class DataRecordDynamicWrapper : DynamicObject, ICustomTypeDescriptor 
{ 
    private IDataRecord _dataRecord; 
    private PropertyDescriptorCollection _properties; 

    // 
    // (existing members) 
    // 

    PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties() 
    { 
     if (_properties == null) 
      _properties = GenerateProperties(); 
     return _properties; 
    } 

    private PropertyDescriptorCollection GenerateProperties() 
    { 
     var count = _dataRecord.FieldCount; 
     var properties = new PropertyDescriptor[count]; 

     for (var i = 0; i < count; i++) 
     { 
      properties[i] = new DataRecordProperty(
       i, 
       _dataRecord.GetName(i), 
       _dataRecord.GetFieldType(i)); 
     } 

     return new PropertyDescriptorCollection(properties); 
    } 

    // 
    // (implement other ICustomTypeDescriptor members...) 
    // 

    private sealed class DataRecordProperty : PropertyDescriptor 
    { 
     private static readonly Attribute[] NoAttributes = new Attribute[0]; 

     private readonly int _ordinal; 
     private readonly Type _type; 

     public DataRecordProperty(int ordinal, string name, Type type) 
      : base(name, NoAttributes) 
     { 
      _ordinal = ordinal; 
      _type = type; 
     } 

     public override bool CanResetValue(object component) 
     { 
      return false; 
     } 

     public override object GetValue(object component) 
     { 
      var wrapper = ((DataRecordDynamicWrapper)component); 
      return wrapper._dataRecord.GetValue(_ordinal); 
     } 

     public override void ResetValue(object component) 
     { 
      throw new NotSupportedException(); 
     } 

     public override void SetValue(object component, object value) 
     { 
      throw new NotSupportedException(); 
     } 

     public override bool ShouldSerializeValue(object component) 
     { 
      return true; 
     } 

     public override Type ComponentType 
     { 
      get { return typeof(IDataRecord); } 
     } 

     public override bool IsReadOnly 
     { 
      get { return true; } 
     } 

     public override Type PropertyType 
     { 
      get { return _type; } 
     } 
    } 
} 

당신은 다음 EnumToDataTable() 방법을 수정할 수 :

반사를 사용하는 대신

, 당신은 당신에게 당신의 데이터 레코드의 속성을 노출하는 방법 (complete example here)를 제공하는, 당신의 DataRecordDynamicWrapperICustomTypeDescriptor을 구현 할 수 System.Reflection 대신 System.ComponenetModel API를 사용하기 :

public static DataTable EnumToDataTable<T>(IEnumerable<T> l_oItems) 
{ 
    var firstItem = l_oItems.FirstOrDefault(); 
    if (firstItem == null) 
     return new DataTable(); 

    DataTable oReturn = new DataTable(TypeDescriptor.GetClassName(firstItem)); 
    object[] a_oValues; 
    int i; 

    var properties = TypeDescriptor.GetProperties(firstItem); 

    foreach (PropertyDescriptor property in properties) 
    { 
     oReturn.Columns.Add(property.Name, BaseType(property.PropertyType)); 
    } 

    //#### Traverse the l_oItems 
    foreach (T oItem in l_oItems) 
    { 
     //#### Collect the a_oValues for this loop 
     a_oValues = new object[properties.Count]; 

     //#### Traverse the a_oProperties, populating each a_oValues as we go 
     for (i = 0; i < properties.Count; i++) 
      a_oValues[i] = properties[i].GetValue(oItem); 

     //#### .Add the .Row that represents the current a_oValues into our oReturn value 
     oReturn.Rows.Add(a_oValues); 
    } 

    //#### Return the above determined oReturn value to the caller 
    return oReturn; 
} 

거꾸로을이 방법으로하는 것은을 것입니다 10은 ICustomTypeDescriptor을 구현하지 않는 항목의 표준 유형 설명자로 되돌아갑니다 (예 : 일반 오래된 CLR 객체의 경우 원래 코드와 비슷하게 동작 함).

+0

Wao, 정말 좋은 설명과 답변. 당신은 나 한테서 한가지의 자격이있어. :) –

0

배열 :

여기 내 코드입니다. 실제로 documentation에 따르면 GetProperties() 메서드는 현재 Type의 모든 public 속성을 반환합니다.

public DataRecordDynamicWrapper(IDataRecord dataRecord) { _dataRecord = dataRecord; } 

을하지만 방법입니다 :

속성으로 유일하게 가능한 후보가 될 수 있습니다. 또한 메서드/속성 이름이 없습니다.

프로퍼티는, 귀하의 경우, 다음과 같이 선언한다 : 속성 here에 대한

private IDataRecord _dataRecord; 

public IDataRecord DataRecord 
{ 
    set{ 
    _dataRecord = value; 
    } 
    get{ 
    return _dataRecord; 
    } 
} 

더 많은 정보를 원하시면.

관련 문제