Created
March 30, 2011 16:12
-
-
Save darrenkopp/894695 to your computer and use it in GitHub Desktop.
This file contains 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
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) | |
} | |
} | |
} | |
} |
This file contains 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
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