Skip to content

Commit

Permalink
Enhancement for projecting into new type class
Browse files Browse the repository at this point in the history
Signed-off-by: funky81 <funky81.milis@gmail.com>
  • Loading branch information
funky81 committed Aug 18, 2009
1 parent ee55f4a commit aa7a9c1
Show file tree
Hide file tree
Showing 5 changed files with 110 additions and 65 deletions.
36 changes: 24 additions & 12 deletions SubSonic.Core/Extensions/Database.cs
Expand Up @@ -138,10 +138,7 @@ public static List<Query.Constraint> ToConstraintList(this object value)
return query.Constraints;
}

/// <summary>
/// Coerces an IDataReader to try and load an object using name/property matching
/// </summary>
public static void Load<T>(this IDataReader rdr, T item)
public static void Load<T>(this IDataReader rdr, T item, List<string> ColumnNames)
{
Type iType = typeof(T);

Expand All @@ -155,10 +152,11 @@ public static void Load<T>(this IDataReader rdr, T item)
{
string pName = rdr.GetName(i);
currentProp = cachedProps.SingleOrDefault(x => (x.Name.EndsWith("X") ? x.Name.Chop(1) : x.Name).Equals(pName, StringComparison.InvariantCultureIgnoreCase));

if (currentProp == null)
/** maybe this is projection **/
currentProp = cachedProps[i];

if (currentProp == null && ColumnNames.Count != 0)
{
currentProp = cachedProps.First(x => x.Name == ColumnNames[i]);
}

//if the property is null, likely it's a Field
if (currentProp == null)
Expand Down Expand Up @@ -199,6 +197,13 @@ public static void Load<T>(this IDataReader rdr, T item)
}

}
/// <summary>
/// Coerces an IDataReader to try and load an object using name/property matching
/// </summary>
public static void Load<T>(this IDataReader rdr, T item)
{
Load(rdr, item, null);
}

/// <summary>
/// Loads a single primitive value type
Expand Down Expand Up @@ -266,12 +271,19 @@ private static bool IsCoreSystemType(Type type)
type == typeof(bool?);
}

public static IEnumerable<T> ToEnumerable<T>(this IDataReader rdr)
{
return ToEnumerable<T>(rdr);
}

/// <summary>
/// Coerces an IDataReader to load an enumerable of T
/// Make into Enumerable
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="rdr"></param>
public static IEnumerable<T> ToEnumerable<T>(this IDataReader rdr)
/// <param name="rdr">The RDR.</param>
/// <param name="ColumnNames">The column names.</param>
/// <returns></returns>
public static IEnumerable<T> ToEnumerable<T>(this IDataReader rdr, List<string> ColumnNames)
{
List<T> result = new List<T>();
while (rdr.Read())
Expand Down Expand Up @@ -302,7 +314,7 @@ public static IEnumerable<T> ToEnumerable<T>(this IDataReader rdr)
instance = Activator.CreateInstance<T>();

//do we have a parameterless constructor?
Load(rdr, instance);
Load(rdr, instance, ColumnNames);
result.Add(instance);
}
return result.AsEnumerable();
Expand Down
45 changes: 25 additions & 20 deletions SubSonic.Core/Linq/Structure/DbQueryProvider.cs
Expand Up @@ -41,7 +41,7 @@ public DbQueryProvider(IDataProvider provider, QueryPolicy paramPolicy, TextWrit
language = mapping.Language;
//log = log;
}

public DbQueryProvider(IDataProvider provider)
{
_provider = provider;
Expand All @@ -52,7 +52,7 @@ public DbQueryProvider(IDataProvider provider)
case DataClient.MySqlClient:
lang = new MySqlLanguage(_provider);
break;
case DataClient.SQLite:
case DataClient.SQLite:
lang = new SqliteLanguage(_provider);
break;
default:
Expand Down Expand Up @@ -126,7 +126,7 @@ public override object Execute(Expression expression)
else
{
// compile the execution plan and invoke it
Expression<Func<object>> efn = ConvertThis(plan,typeof(object));
Expression<Func<object>> efn = ConvertThis(plan, typeof(object));
Func<object> fn = efn.Compile();
return fn();
}
Expand Down Expand Up @@ -247,51 +247,56 @@ public virtual IEnumerable<T> Execute<T>(QueryCommand<T> query, object[] paramVa
//DbDataReader reader = cmd.ExecuteReader();
//return Project(reader, query.Projector);



QueryCommand cmd = new QueryCommand(query.CommandText, _provider);
for (int i = 0; i < paramValues.Length; i++)
{

//need to assign a DbType
var valueType = paramValues[i].GetType();
var dbType = Database.GetDbType(valueType);
cmd.AddParameter(query.ParameterNames[i], paramValues[i],dbType);


cmd.AddParameter(query.ParameterNames[i], paramValues[i], dbType);
}
/*
var reader = _provider.ExecuteReader(cmd);
var result = Project(reader, query.Projector);
return result;
*/
/*
var reader = _provider.ExecuteReader(cmd);
var result = Project(reader, query.Projector);
return result;
*/

IEnumerable<T> result;
Type type = typeof (T);
Type type = typeof(T);
//this is so hacky - the issue is that the Projector below uses Expression.Convert, which is a bottleneck
//it's about 10x slower than our ToEnumerable. Our ToEnumerable, however, stumbles on Anon types and groupings
//since it doesn't know how to instantiate them (I tried - not smart enough). So we do some trickery here.
if (type.Name.Contains("AnonymousType") || type.Name.StartsWith("Grouping`") || type.FullName.StartsWith("System.")) {
if (type.Name.Contains("AnonymousType") || type.Name.StartsWith("Grouping`") || type.FullName.StartsWith("System."))
{
var reader = _provider.ExecuteReader(cmd);
result = Project(reader, query.Projector);
} else
}
else
{

using (var reader = _provider.ExecuteReader(cmd))
{

//use our reader stuff
//thanks to Pascal LaCroix for the help here...
var resultType = typeof (T);
var resultType = typeof(T);

var test = mapping.GetMappedMembers(resultType);

if (resultType.IsValueType)
{
result = reader.ToEnumerableValueType<T>();

}
else
{
result = reader.ToEnumerable<T>();

if (query.ColumnNames.Count != 0)
result = reader.ToEnumerable<T>(query.ColumnNames);
else
result = reader.ToEnumerable<T>();
}
}
}
Expand Down
52 changes: 27 additions & 25 deletions SubSonic.Core/Linq/Structure/ExecutionBuilder.cs
Expand Up @@ -74,8 +74,8 @@ private static Expression MakeSequence(IList<Expression> expressions)
Expression last = expressions[expressions.Count - 1];
return
Expression.Convert(
Expression.Call(typeof (ExecutionBuilder), "Sequence", null,
Expression.NewArrayInit(typeof (object), expressions)), last.Type);
Expression.Call(typeof(ExecutionBuilder), "Sequence", null,
Expression.NewArrayInit(typeof(object), expressions)), last.Type);
}

public static object Sequence(params object[] values)
Expand All @@ -85,7 +85,7 @@ public static object Sequence(params object[] values)

private static Expression MakeAssign(Expression variable, Expression value)
{
return Expression.Call(typeof (ExecutionBuilder), "Assign", new[] {variable.Type}, variable, value);
return Expression.Call(typeof(ExecutionBuilder), "Assign", new[] { variable.Type }, variable, value);
}

public static T Assign<T>(ref T variable, T value)
Expand Down Expand Up @@ -121,11 +121,11 @@ private static Expression MakeJoinKey(IList<Expression> key)
return key[0];

return
Expression.New(typeof (CompoundKey).GetConstructors()[0],
Expression.NewArrayInit(typeof (object),
Expression.New(typeof(CompoundKey).GetConstructors()[0],
Expression.NewArrayInit(typeof(object),
key.Select(k =>
(Expression)
Expression.Convert(k, typeof (object
Expression.Convert(k, typeof(object
)))));
}

Expand All @@ -138,8 +138,8 @@ protected override Expression VisitClientJoin(ClientJoinExpression join)
Expression outerKey = MakeJoinKey(join.OuterKey);

ConstructorInfo kvpConstructor =
typeof (KeyValuePair<,>).MakeGenericType(innerKey.Type, join.Projection.Projector.Type).GetConstructor(
new[] {innerKey.Type, join.Projection.Projector.Type});
typeof(KeyValuePair<,>).MakeGenericType(innerKey.Type, join.Projection.Projector.Type).GetConstructor(
new[] { innerKey.Type, join.Projection.Projector.Type });
Expression constructKVPair = Expression.New(kvpConstructor, innerKey, join.Projection.Projector);
ProjectionExpression newProjection = new ProjectionExpression(join.Projection.Source, constructKVPair);

Expand All @@ -149,7 +149,7 @@ protected override Expression VisitClientJoin(ClientJoinExpression join)
ParameterExpression kvp = Expression.Parameter(constructKVPair.Type, "kvp");

// filter out nulls
if (join.Projection.Projector.NodeType == (ExpressionType) DbExpressionType.OuterJoined)
if (join.Projection.Projector.NodeType == (ExpressionType)DbExpressionType.OuterJoined)
{
LambdaExpression pred = Expression.Lambda(
Expression.NotEqual(
Expand All @@ -158,14 +158,14 @@ protected override Expression VisitClientJoin(ClientJoinExpression join)
),
kvp
);
execution = Expression.Call(typeof (Enumerable), "Where", new[] {kvp.Type}, execution, pred);
execution = Expression.Call(typeof(Enumerable), "Where", new[] { kvp.Type }, execution, pred);
}

// make lookup
LambdaExpression keySelector = Expression.Lambda(Expression.PropertyOrField(kvp, "Key"), kvp);
LambdaExpression elementSelector = Expression.Lambda(Expression.PropertyOrField(kvp, "Value"), kvp);
Expression toLookup = Expression.Call(typeof (Enumerable), "ToLookup",
new[] {kvp.Type, outerKey.Type, join.Projection.Projector.Type},
Expression toLookup = Expression.Call(typeof(Enumerable), "ToLookup",
new[] { kvp.Type, outerKey.Type, join.Projection.Projector.Type },
execution, keySelector, elementSelector);

// 2) agg(lookup[outer])
Expand Down Expand Up @@ -200,24 +200,26 @@ private Expression ExecuteProjection(ProjectionExpression projection, bool okayT
okayToDefer &= (receivingMember != null && policy.IsDeferLoaded(receivingMember));

// parameterize query
projection = (ProjectionExpression) Parameterizer.Parameterize(projection);
projection = (ProjectionExpression)Parameterizer.Parameterize(projection);

if (scope != null)
{
// also convert references to outer alias to named values! these become SQL parameters too
projection = (ProjectionExpression) OuterParameterizer.Parameterize(scope.Alias, projection);
projection = (ProjectionExpression)OuterParameterizer.Parameterize(scope.Alias, projection);
}

var saveScope = scope;
ParameterExpression reader = Expression.Parameter(typeof (DbDataReader), "r" + nReaders++);
ParameterExpression reader = Expression.Parameter(typeof(DbDataReader), "r" + nReaders++);
scope = new Scope(scope, reader, projection.Source.Alias, projection.Source.Columns);
LambdaExpression projector = Expression.Lambda(Visit(projection.Projector), reader);
scope = saveScope;

List<string> columnNames = ColumnNamedGatherer.Gather(projector.Body);

string commandText = policy.Mapping.Language.Format(projection.Source);
ReadOnlyCollection<NamedValueExpression> namedValues = NamedValueGatherer.Gather(projection.Source);
string[] names = namedValues.Select(v => v.Name).ToArray();
Expression[] values = namedValues.Select(v => Expression.Convert(Visit(v.Value), typeof (object))).ToArray();
Expression[] values = namedValues.Select(v => Expression.Convert(Visit(v.Value), typeof(object))).ToArray();

string methExecute = okayToDefer
? "ExecuteDeferred"
Expand All @@ -228,15 +230,15 @@ private Expression ExecuteProjection(ProjectionExpression projection, bool okayT
}

// call low-level execute directly on supplied DbQueryProvider
Expression result = Expression.Call(provider, methExecute, new[] {projector.Body.Type},
Expression result = Expression.Call(provider, methExecute, new[] { projector.Body.Type },
Expression.New(
typeof (QueryCommand<>).MakeGenericType(projector.Body.Type).
typeof(QueryCommand<>).MakeGenericType(projector.Body.Type).
GetConstructors()[0],
Expression.Constant(commandText),
Expression.Constant(names),
projector
projector, Expression.Constant(columnNames)
),
Expression.NewArrayInit(typeof (object), values)
Expression.NewArrayInit(typeof(object), values)
);

if (projection.Aggregator != null)
Expand All @@ -251,7 +253,7 @@ private Expression ExecuteProjection(ProjectionExpression projection, bool okayT
protected override Expression VisitOuterJoined(OuterJoinedExpression outer)
{
Expression expr = Visit(outer.Expression);
ColumnExpression column = (ColumnExpression) outer.Test;
ColumnExpression column = (ColumnExpression)outer.Test;
ParameterExpression reader;
int iOrdinal;
if (scope.TryGetValue(column, out reader, out iOrdinal))
Expand Down Expand Up @@ -285,7 +287,7 @@ protected override Expression VisitColumn(ColumnExpression column)
// 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(typeof(Convert), "ChangeType", null,
Expression.Call(reader, "GetValue", null, Expression.Constant(iOrdinal)),
Expression.Constant(TypeHelper.GetNonNullableType(column.Type))
),
Expand Down Expand Up @@ -317,13 +319,13 @@ private class OuterParameterizer : DbExpressionVisitor

internal static Expression Parameterize(TableAlias outerAlias, Expression expr)
{
OuterParameterizer op = new OuterParameterizer {outerAlias = outerAlias};
OuterParameterizer op = new OuterParameterizer { outerAlias = outerAlias };
return op.Visit(expr);
}

protected override Expression VisitProjection(ProjectionExpression proj)
{
SelectExpression select = (SelectExpression) Visit(proj.Source);
SelectExpression select = (SelectExpression)Visit(proj.Source);
if (select != proj.Source)
{
return new ProjectionExpression(select, proj.Projector, proj.Aggregator);
Expand Down Expand Up @@ -363,7 +365,7 @@ private class Scope
this.outer = outer;
dbDataReader = dbDataReaderParam;
Alias = alias;
nameMap = columns.Select((c, i) => new {c, i}).ToDictionary(x => x.c.Name, x => x.i);
nameMap = columns.Select((c, i) => new { c, i }).ToDictionary(x => x.c.Name, x => x.i);
}

internal TableAlias Alias { get; private set; }
Expand Down
6 changes: 5 additions & 1 deletion SubSonic.Core/Linq/Structure/QueryCommand.cs
Expand Up @@ -8,16 +8,20 @@
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Data.Common;
using System.Data;

namespace SubSonic.Linq.Structure
{
public class QueryCommand<T>
{
public QueryCommand(string commandText, IEnumerable<string> paramNames, Func<DbDataReader, T> projector)
public List<string> ColumnNames = new List<string>();
public QueryCommand(string commandText, IEnumerable<string> paramNames, Func<DbDataReader, T> projector,List<string> ColumnNames)
{
CommandText = commandText;
ParameterNames = new List<string>(paramNames).AsReadOnly();
Projector = projector;
this.ColumnNames = ColumnNames;

}

public string CommandText { get; private set; }
Expand Down

0 comments on commit aa7a9c1

Please sign in to comment.