Skip to content

Instantly share code, notes, and snippets.

@darrenkopp
Created March 30, 2011 16:12
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save darrenkopp/894695 to your computer and use it in GitHub Desktop.
Save darrenkopp/894695 to your computer and use it in GitHub Desktop.
public class DatabaseFactory
{
public static IEnumerable<T> ExecuteQuery<T>(string commandText, CommandType commandType, CommandBehavior behavior, IList<SqlParameter> parameters)
{
using (var reader = ExecuteReader(commandText, commandType, behavior, parameters))
{
var translator = DataTranslator.CreateTranslator<T>(reader);
while (reader.Read())
{
yield return translator(reader)
}
}
}
}
public class DataTranslator
{
public static Func<IDataRecord, T> CreateTranslator<T>(IDataRecord record)
{
Type targetType = typeof(T);
string cacheKey = string.Format("{0}-Translator", targetType);
var cachedTranslator = WebContextCache.GetItem<Func<IDataRecord, T>>(cacheKey);
if (cachedTranslator != null)
return cachedTranslator;
// 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
);
var result = translator.Compile();
WebContextCache.AddItem<Func<IDataRecord, T>>(cacheKey, result);
return result;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment