2014-01-21 244 views
6

我查询到数据库以获取数据。它可能有多于一行。我将它们保存到IEnumerable中。将IEnumerable <dynamic>转换为DataTable

为什么动态?因为我可能会在表中添加新列,我不想更改我的代码以适应它。

然后,我将IEnumerable转换为数据表。我有一个问题来获取动态对象内的属性。任何人都可以帮助我?

这里是我的代码:

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类中定义的属性,该类定义了没有属性(因此为空数组)。

作为替代使用反射,你可以有你的DataRecordDynamicWrapper实施ICustomTypeDescriptor,它给你的方式暴露您的数据记录的属性(complete example here):

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()方法使用System.ComponenetModel的API,而不是System.Reflection

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; 
} 

的上攻这种做法是EnumToDataTable()将回落到没有实现ICustomTypeDescriptor的项目的标准类型描述符(例如,对于普通的旧CLR对象,其行为将类似于您的原始代码)。

+0

Wao,一个很好的解释和答案。你应该从我这里得到一点:) –

0

数组a_oProperties是空的,因为你没有在类DataRecordDynamicWrapper声明的公共财产。事实上,根据documentationGetProperties()方法返回当前类型的所有公共属性。

唯一可能的候选人财产可能是:

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

,但它是一种方法。此外,方法/属性名称丢失。

的属性,你的情况,应声明如下:

private IDataRecord _dataRecord; 

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

有关属性here更多信息。

相关问题