Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue 258 #280

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 51 additions & 11 deletions SubSonic.Core/Linq/Structure/ExecutionBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ namespace SubSonic.Linq.Structure
/// </summary>
public class ExecutionBuilder : DbExpressionVisitor
{
private static readonly IDictionary<Type, Expression> defaultValueActiviationDictionary = new Dictionary<Type, Expression>();
private static readonly IDictionary<Type, ConstantExpression> nonNullableTypeExpressionDictionary = new Dictionary<Type, ConstantExpression>();
private static readonly IDictionary<Type, string> readerGetValue;

private readonly List<Expression> initializers = new List<Expression>();
private readonly QueryPolicy policy;
private readonly Expression provider;
Expand All @@ -30,6 +34,22 @@ public class ExecutionBuilder : DbExpressionVisitor
private MemberInfo receivingMember;
private Scope scope;

static ExecutionBuilder()
{
readerGetValue = new Dictionary<Type, string>();
readerGetValue.Add(typeof(Boolean), "GetBoolean");
readerGetValue.Add(typeof(Byte), "GetByte");
readerGetValue.Add(typeof(DateTime), "GetDateTime");
readerGetValue.Add(typeof(Decimal), "GetDecimal");
readerGetValue.Add(typeof(Double), "GetDouble");
readerGetValue.Add(typeof(Single), "GetFloat");
readerGetValue.Add(typeof(Guid), "GetGuid");
readerGetValue.Add(typeof(Int16), "GetInt16");
readerGetValue.Add(typeof(Int32), "GetInt32");
readerGetValue.Add(typeof(Int64), "GetInt64");
readerGetValue.Add(typeof(String), "GetString");
}

private ExecutionBuilder(QueryPolicy policy, Expression provider)
{
this.policy = policy;
Expand Down Expand Up @@ -298,19 +318,39 @@ protected override Expression VisitColumn(ColumnExpression column)
}
else
{
defvalue = Expression.Constant(Activator.CreateInstance(column.Type), column.Type);
// Using a dictionary to cache default types increases performance 4 - 5%
if (!defaultValueActiviationDictionary.TryGetValue(column.Type, out defvalue))
{
defvalue = Expression.Constant(Activator.CreateInstance(column.Type), column.Type);
defaultValueActiviationDictionary.Add(column.Type, defvalue);
}
}

// this sucks, but since we don't track true SQL types through the query, and ADO throws exception if you
// call the wrong accessor, the best we can do is call GetValue and Convert.ChangeType
Expression value = Expression.Convert(
Expression.Call(typeof (Convert), "ChangeType", null,
Expression.Call(reader, "GetValue", null, Expression.Constant(iOrdinal)),
Expression.Constant(TypeHelper.GetNonNullableType(column.Type), typeof(Type))
),
column.Type
);

// If we know what method to call on DbDataReader (as defined by the dictionary),
// then use that method. (Int32 => GetInt32, String => GetString, etc.) If we don't
// know the type, then default to our original code.
Expression value;
string methodName;
if (readerGetValue.TryGetValue(column.Type, out methodName))
{
value = Expression.Call(reader, methodName, null, Expression.Constant(iOrdinal));
}
else
{
ConstantExpression typeConstantExpression;
if (!nonNullableTypeExpressionDictionary.TryGetValue(column.Type, out typeConstantExpression))
{
typeConstantExpression = Expression.Constant(TypeHelper.GetNonNullableType(column.Type), typeof(Type));
nonNullableTypeExpressionDictionary.Add(column.Type, typeConstantExpression);
}
value = Expression.Convert(
Expression.Call(typeof(Convert), "ChangeType", null,
Expression.Call(reader, "GetValue", null, Expression.Constant(iOrdinal)),
typeConstantExpression
),
column.Type
);
}
return Expression.Condition(
Expression.Call(reader, "IsDBNull", null, Expression.Constant(iOrdinal)),
defvalue, value
Expand Down