Skip to content

Instantly share code, notes, and snippets.

@mstrobel
Last active December 19, 2021 13:08
Show Gist options
  • Save mstrobel/8541557 to your computer and use it in GitHub Desktop.
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
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