Last active
December 19, 2021 13:08
-
-
Save mstrobel/8541557 to your computer and use it in GitHub Desktop.
DynamicObject wrapping IDataRecord and implementing ICustomTypeDescriptor. In response to this StackOverflow question: http://stackoverflow.com/questions/21256655/convert-ienumerabledynamic-to-datatable
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.ComponentModel; | |
using System.Data; | |
using System.Dynamic; | |
using System.Linq; | |
using System.Xml; | |
namespace DynamicDescriptor | |
{ | |
internal class Program | |
{ | |
private static void Main() | |
{ | |
var records = Execute(); | |
var dataTable = EnumToDataTable(records); | |
dataTable.WriteXml(new XmlTextWriter(Console.Out)); | |
} | |
public static DataTable EnumToDataTable<T>(IEnumerable<T> l_oItems) | |
{ | |
var firstItem = l_oItems.FirstOrDefault(); | |
if (firstItem == null) | |
return new DataTable(); | |
var 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 (var 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; | |
} | |
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 | |
return oType; | |
} | |
// Method to Execute Query | |
public static IEnumerable<dynamic> Execute() | |
{ | |
var result = new IDataRecord[] { new DummyRecord(0), new DummyRecord(1), new DummyRecord(2) }; | |
foreach (var record in result) | |
yield return new DataRecordDynamicWrapper(record); | |
} | |
public class DataRecordDynamicWrapper : DynamicObject, ICustomTypeDescriptor | |
{ | |
private readonly IDataRecord _dataRecord; | |
private PropertyDescriptorCollection _properties; | |
public DataRecordDynamicWrapper(IDataRecord dataRecord) | |
{ | |
_dataRecord = dataRecord; | |
} | |
public override bool TryGetMember(GetMemberBinder binder, out object result) | |
{ | |
result = _dataRecord[binder.Name]; | |
return result != null; | |
} | |
AttributeCollection ICustomTypeDescriptor.GetAttributes() | |
{ | |
return AttributeCollection.Empty; | |
} | |
string ICustomTypeDescriptor.GetClassName() | |
{ | |
return _dataRecord.GetType().Name; | |
} | |
string ICustomTypeDescriptor.GetComponentName() | |
{ | |
return _dataRecord.GetType().Name; | |
} | |
TypeConverter ICustomTypeDescriptor.GetConverter() | |
{ | |
return new TypeConverter(); | |
} | |
EventDescriptor ICustomTypeDescriptor.GetDefaultEvent() | |
{ | |
return null; | |
} | |
PropertyDescriptor ICustomTypeDescriptor.GetDefaultProperty() | |
{ | |
return null; | |
} | |
object ICustomTypeDescriptor.GetEditor(Type editorBaseType) | |
{ | |
throw new NotSupportedException(); | |
} | |
EventDescriptorCollection ICustomTypeDescriptor.GetEvents() | |
{ | |
return EventDescriptorCollection.Empty; | |
} | |
EventDescriptorCollection ICustomTypeDescriptor.GetEvents(Attribute[] attributes) | |
{ | |
return EventDescriptorCollection.Empty; | |
} | |
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); | |
} | |
PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties(Attribute[] attributes) | |
{ | |
if (attributes != null && attributes.Length == 0) | |
return ((ICustomTypeDescriptor)this).GetProperties(); | |
return PropertyDescriptorCollection.Empty; | |
} | |
object ICustomTypeDescriptor.GetPropertyOwner(PropertyDescriptor pd) | |
{ | |
return _dataRecord; | |
} | |
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; } | |
} | |
} | |
} | |
internal sealed class DummyRecord : IDataRecord | |
{ | |
private readonly int _id; | |
public DummyRecord(int id) | |
{ | |
_id = id; | |
} | |
public string GetName(int i) | |
{ | |
return "Property" + i; | |
} | |
public string GetDataTypeName(int i) | |
{ | |
return "String"; | |
} | |
public Type GetFieldType(int i) | |
{ | |
return typeof(string); | |
} | |
public object GetValue(int i) | |
{ | |
return "Value_" + _id + "_" + i; | |
} | |
public int GetValues(object[] values) | |
{ | |
return 3; | |
} | |
public int GetOrdinal(string name) | |
{ | |
if (name.StartsWith("Property")) | |
return int.Parse(name.Remove(0, "Property".Length)); | |
return -1; | |
} | |
public bool GetBoolean(int i) | |
{ | |
return false; | |
} | |
public byte GetByte(int i) | |
{ | |
return default(byte); | |
} | |
public long GetBytes(int i, long fieldOffset, byte[] buffer, int bufferoffset, int length) | |
{ | |
throw new NotSupportedException(); | |
} | |
public char GetChar(int i) | |
{ | |
return default(char); | |
} | |
public long GetChars(int i, long fieldoffset, char[] buffer, int bufferoffset, int length) | |
{ | |
throw new NotSupportedException(); | |
} | |
public Guid GetGuid(int i) | |
{ | |
return default(Guid); | |
} | |
public short GetInt16(int i) | |
{ | |
return default(short); | |
} | |
public int GetInt32(int i) | |
{ | |
return default(int); | |
} | |
public long GetInt64(int i) | |
{ | |
return default(long); | |
} | |
public float GetFloat(int i) | |
{ | |
return default(float); | |
} | |
public double GetDouble(int i) | |
{ | |
return default(double); | |
} | |
public string GetString(int i) | |
{ | |
return (string)GetValue(i); | |
} | |
public decimal GetDecimal(int i) | |
{ | |
return default(decimal); | |
} | |
public DateTime GetDateTime(int i) | |
{ | |
return default(DateTime); | |
} | |
public IDataReader GetData(int i) | |
{ | |
throw new NotSupportedException(); | |
} | |
public bool IsDBNull(int i) | |
{ | |
return false; | |
} | |
public int FieldCount | |
{ | |
get { return 3; } | |
} | |
object IDataRecord.this[int i] | |
{ | |
get { return GetValue(i); } | |
} | |
object IDataRecord.this[string name] | |
{ | |
get { return GetValue(GetOrdinal(name)); } | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment