Skip to content

Instantly share code, notes, and snippets.

@darrenkopp
Last active December 14, 2015 01:28
Show Gist options
  • Save darrenkopp/5005932 to your computer and use it in GitHub Desktop.
Save darrenkopp/5005932 to your computer and use it in GitHub Desktop.
Some handy utilities for dealing with ado.net
public static class DataReaderExtensions
{
public static IEnumerable<T> Stream<T>(this IDbCommand command, Func<IDataRecord, T> converter)
{
using (var reader = command.ExecuteReader(CommandBehavior.SingleResult))
{
while (reader.Read())
yield return converter(reader);
}
}
public static Nullable<T> ValueOrNull<T>(this IDataRecord record, string field) where T : struct
{
var value = record[field];
if (value == DBNull.Value)
return new Nullable<T>();
return (T)value;
}
public static T ValueOrDefault<T>(this IDataRecord record, string field)
{
var value = record[field];
if (value == DBNull.Value)
return default(T);
return (T)value;
}
public static long? Long(this IDataRecord record, string column)
{
var value = record[column];
if (value == DBNull.Value)
return null;
return (long)value;
}
public static string String(this IDataRecord record, string column)
{
var value = record[column];
if (value == DBNull.Value)
return null;
return (string)value;
}
public static Guid? Guid(this IDataRecord record, string column)
{
var value = record[column];
if (value == DBNull.Value)
return null;
return (Guid)value;
}
public static DateTime? Date(this IDataRecord record, string column)
{
var value = record[column];
if (value == DBNull.Value)
return null;
return (DateTime)value;
}
public static int? Int(this IDataRecord record, string column)
{
var value = record[column];
if (value == DBNull.Value)
return null;
return (int)value;
}
public static bool? Boolean(this IDataRecord record, string column)
{
var value = record[column];
if (value == DBNull.Value)
return null;
if (value is bool)
return (bool)value;
if (value is int)
return ((int)value) == 1;
return Convert.ToBoolean(value);
}
public static long? RowVersion(this IDataRecord record, string column)
{
object value = record[column];
if (value == DBNull.Value)
return null;
// can't use BitConverter as value coming out is big-endian
byte[] bytes = (byte[])value;
long ret = 0;
for (int i = 0; i < 8; i++)
{
ret = unchecked((ret << 8) | bytes[i]);
}
return ret;
}
public static decimal? Decimal(this IDataRecord record, string column)
{
var value = record[column];
if (value == DBNull.Value)
return null;
return (decimal)value;
}
public static byte[] Binary(this IDataRecord record, string column)
{
int idx = record.GetOrdinal(column);
if (record.IsDBNull(idx))
return null;
// copy data into memory
using (var memory = new MemoryStream())
{
int bufferSize = 4096;
byte[] buffer = new byte[bufferSize];
int offset = 0;
while (true)
{
// read in the data and increment our offset
long length = record.GetBytes(idx, offset, buffer, 0, bufferSize);
offset += bufferSize;
// write the resulting bytes to the memory stream
memory.Write(buffer, 0, (int)length);
// if we didn't read full buffer, then we are likely done
if (length < bufferSize)
break;
}
return memory.ToArray();
}
}
}
public class DataTranslator
{
public static Func<IDataRecord, T> CreateTranslator<T>(IDataRecord record)
{
// get all properties that are translated
var propertyDictionary = targetType.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.SetProperty)
.Where(p => !Attribute.IsDefined(p, typeof(NotTranslatedAttribute)))
.ToDictionary(p => p.Name.ToLower());
var recordMappings = new Dictionary<int, PropertyInfo>();
for (int i = 0; i < record.FieldCount; i++)
{
string fieldName = record.GetName(i).ToLower();
if (propertyDictionary.ContainsKey(fieldName))
recordMappings.Add(i, propertyDictionary[fieldName]);
}
// Create Methods to GetValue and check for null from record
Type dataRecordType = typeof(IDataRecord);
MethodInfo recordGetValue = dataRecordType.GetMethod("GetValue");
MethodInfo recordIsDBNull = dataRecordType.GetMethod("IsDBNull");
// create parameter for record
ParameterExpression rec = Expression.Parameter(dataRecordType, "record");
Expression<Func<IDataRecord, T>> translator = Expression.Lambda<Func<IDataRecord, T>>(
Expression.MemberInit(Expression<T>.New(targetType),
recordMappings.Select(mapping =>
{
// property can handle a null value
if ((mapping.Value.PropertyType.IsValueType == false) || (mapping.Value.PropertyType.IsGenericType && mapping.Value.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>)))
{
var conversion = Expression.Condition(
Expression<bool>.Call(rec, recordIsDBNull, Expression<int>.Constant(mapping.Key)),
Expression.TypeAs(Expression.Constant(null), mapping.Value.PropertyType),
Expression.TypeAs(Expression.Call(rec, recordGetValue, Expression<int>.Constant(mapping.Key)), mapping.Value.PropertyType)
);
return Expression.Bind(mapping.Value, conversion) as MemberBinding;
}
// handle enumerations
if (mapping.Value.PropertyType.IsEnum)
{
// converts the type returned by the record to that of the underlying type of the enumeration
Expression<Func<object, Type, object>> converter = (item, toType) => Convert.ChangeType(item, toType);
var underlyingType = Enum.GetUnderlyingType(mapping.Value.PropertyType);
var fieldType = record.GetFieldType(mapping.Key);
// by default, the value for conversion is what we get from the record.GetValue method call
Expression valueForConversionExpression = Expression.Call(rec, recordGetValue, Expression<int>.Constant(mapping.Key));
if (underlyingType != fieldType)
{
// the field types don't match, so the value we'll pass to the conversion method will be the result
// of the Convert.ChangeType method
valueForConversionExpression = Expression.Invoke(
converter,
new Expression[] {
valueForConversionExpression,
Expression<Type>.Constant(underlyingType)
}
);
}
// return the binding expression
return Expression.Bind(
mapping.Value,
Expression.Convert(
valueForConversionExpression,
mapping.Value.PropertyType
)
) as MemberBinding;
}
// plain ol' struct, just return the conversion
return Expression.Bind(mapping.Value, Expression.Convert(Expression.Call(rec, recordGetValue, Expression<int>.Constant(mapping.Key)), mapping.Value.PropertyType)) as MemberBinding;
})
), rec
);
return translator.Compile();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment