-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Select Projections feature added; Progress towards eager loading; (Se…
- Loading branch information
1 parent
e9319b7
commit 89b7a60
Showing
24 changed files
with
680 additions
and
98 deletions.
There are no files selected for viewing
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
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
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
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
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
110 changes: 110 additions & 0 deletions
110
Achilles.Entities.Sqlite/Linq/EagerFetchingExtensions.cs
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
#region Copyright Notice | ||
|
||
// Copyright (c) by Achilles Software, All rights reserved. | ||
// | ||
// Licensed under the MIT License. See License.txt in the project root for license information. | ||
// | ||
// Send questions regarding this copyright notice to: mailto:[email protected] | ||
|
||
#endregion | ||
|
||
#region Namespaces | ||
|
||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Linq.Expressions; | ||
using System.Reflection; | ||
|
||
#endregion | ||
|
||
namespace Achilles.Entities.Linq | ||
{ | ||
public static class EagerFetchingExtensions | ||
{ | ||
public static FluentFetchRequest<TOriginating, TRelated> FetchMany<TOriginating, TRelated>( | ||
this IQueryable<TOriginating> query, | ||
Expression<Func<TOriginating, IEnumerable<TRelated>>> relatedObjectSelector ) | ||
{ | ||
if ( query == null ) | ||
{ | ||
throw new ArgumentNullException( nameof( query ) ); | ||
} | ||
|
||
if ( relatedObjectSelector == null ) | ||
{ | ||
throw new ArgumentNullException( nameof( relatedObjectSelector ) ); | ||
} | ||
|
||
var methodInfo = ((MethodInfo)MethodBase.GetCurrentMethod()).MakeGenericMethod( typeof( TOriginating ), typeof( TRelated ) ); | ||
|
||
return CreateFluentFetchRequest<TOriginating, TRelated>( methodInfo, query, relatedObjectSelector ); | ||
} | ||
|
||
public static FluentFetchRequest<TOriginating, TRelated> FetchOne<TOriginating, TRelated>( | ||
this IQueryable<TOriginating> query, | ||
Expression<Func<TOriginating, TRelated>> relatedObjectSelector ) | ||
{ | ||
if ( query == null ) | ||
{ | ||
throw new ArgumentNullException( nameof( query ) ); | ||
} | ||
if ( relatedObjectSelector == null ) | ||
{ | ||
throw new ArgumentNullException( nameof( relatedObjectSelector ) ); | ||
} | ||
|
||
var methodInfo = ((MethodInfo)MethodBase.GetCurrentMethod()).MakeGenericMethod( typeof( TOriginating ), typeof( TRelated ) ); | ||
|
||
return CreateFluentFetchRequest<TOriginating, TRelated>( methodInfo, query, relatedObjectSelector ); | ||
} | ||
|
||
public static FluentFetchRequest<TQueried, TRelated> ThenFetchMany<TQueried, TFetch, TRelated>( | ||
this FluentFetchRequest<TQueried, TFetch> query, | ||
Expression<Func<TFetch, IEnumerable<TRelated>>> relatedObjectSelector ) | ||
{ | ||
if ( query == null ) | ||
{ | ||
throw new ArgumentNullException( nameof( query ) ); | ||
} | ||
if ( relatedObjectSelector == null ) | ||
{ | ||
throw new ArgumentNullException( nameof( relatedObjectSelector ) ); | ||
} | ||
|
||
var methodInfo = ((MethodInfo)MethodBase.GetCurrentMethod()).MakeGenericMethod( typeof( TQueried ), typeof( TFetch ), typeof( TRelated ) ); | ||
|
||
return CreateFluentFetchRequest<TQueried, TRelated>( methodInfo, query, relatedObjectSelector ); | ||
} | ||
|
||
public static FluentFetchRequest<TQueried, TRelated> ThenFetchOne<TQueried, TFetch, TRelated>( | ||
this FluentFetchRequest<TQueried, TFetch> query, | ||
Expression<Func<TFetch, TRelated>> relatedObjectSelector ) | ||
{ | ||
if ( query == null ) | ||
{ | ||
throw new ArgumentNullException( nameof( query ) ); | ||
} | ||
if ( relatedObjectSelector == null ) | ||
{ | ||
throw new ArgumentNullException( nameof( relatedObjectSelector ) ); | ||
} | ||
|
||
var methodInfo = ((MethodInfo)MethodBase.GetCurrentMethod()).MakeGenericMethod( typeof( TQueried ), typeof( TFetch ), typeof( TRelated ) ); | ||
|
||
return CreateFluentFetchRequest<TQueried, TRelated>( methodInfo, query, relatedObjectSelector ); | ||
} | ||
|
||
private static FluentFetchRequest<TOriginating, TRelated> CreateFluentFetchRequest<TOriginating, TRelated>( | ||
MethodInfo currentFetchMethod, | ||
IQueryable<TOriginating> query, | ||
LambdaExpression relatedObjectSelector ) | ||
{ | ||
var queryProvider = query.Provider; // ArgumentUtility.CheckNotNullAndType<QueryProviderBase>( "query.Provider", query.Provider ); | ||
|
||
var callExpression = Expression.Call( currentFetchMethod, query.Expression, relatedObjectSelector ); | ||
|
||
return new FluentFetchRequest<TOriginating, TRelated>( queryProvider, callExpression ); | ||
} | ||
} | ||
} |
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
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,14 @@ | ||
#region Namespaces | ||
#region Copyright Notice | ||
|
||
// Copyright (c) by Achilles Software, All rights reserved. | ||
// | ||
// Licensed under the MIT License. See License.txt in the project root for license information. | ||
// | ||
// Send questions regarding this copyright notice to: mailto:[email protected] | ||
|
||
#endregion | ||
|
||
#region Namespaces | ||
|
||
using Achilles.Entities.Extensions; | ||
using Achilles.Entities.Relational; | ||
|
@@ -10,36 +20,68 @@ | |
|
||
namespace Achilles.Entities.Linq.ExpressionVisitors | ||
{ | ||
/// <summary> | ||
/// | ||
/// </summary> | ||
public class SelectExpressionVisitor : SqlExpressionVisitor | ||
{ | ||
#region Fields | ||
|
||
private bool _inProjection = false; | ||
private MemberAssignment _projectionBinding; | ||
|
||
#endregion | ||
|
||
#region Constructor(s) | ||
|
||
public SelectExpressionVisitor( DataContext dbContext, SqlParameterCollection parameters ) | ||
: base( dbContext, parameters ) | ||
{ | ||
} | ||
|
||
#endregion | ||
|
||
public static string GetStatement( DataContext dbContext, SqlParameterCollection parameters, Expression expression ) | ||
{ | ||
var expressionVisitor = new SelectExpressionVisitor( dbContext, parameters ); | ||
var selectExpressionVisitor = new SelectExpressionVisitor( dbContext, parameters ); | ||
|
||
expressionVisitor.Visit( expression ); | ||
selectExpressionVisitor.Visit( expression ); | ||
|
||
return expressionVisitor.GetStatement(); | ||
return selectExpressionVisitor.GetStatement(); | ||
} | ||
|
||
protected override Expression VisitQuerySourceReference( QuerySourceReferenceExpression expression ) | ||
{ | ||
var EntityMapping = _dbContext.Model.GetEntityMapping( expression.ReferencedQuerySource.ItemType ); | ||
// Review: Check for null? | ||
|
||
Statement.AppendEnumerable( | ||
EntityMapping.ColumnMappings.Select( p => p.ColumnName ), | ||
string.Format( "{0}.", | ||
expression.ReferencedQuerySource.ItemName ), | ||
string.Format( ", {0}.", expression.ReferencedQuerySource.ItemName ) ); | ||
if ( _inProjection ) | ||
{ | ||
Statement.AppendEnumerable( | ||
EntityMapping.ColumnMappings.Select( p => p.ColumnName ), | ||
string.Format( "{0}.", expression.ReferencedQuerySource.ItemName ), | ||
", ", | ||
_projectionBinding.Member.Name ); | ||
|
||
} | ||
else | ||
{ | ||
Statement.AppendEnumerable( | ||
EntityMapping.ColumnMappings.Select( p => p.ColumnName ), | ||
string.Format( "{0}.", expression.ReferencedQuerySource.ItemName ), | ||
", " ); | ||
} | ||
|
||
return expression; | ||
} | ||
|
||
/// <summary> | ||
/// The MemberInit expression creates a new object and initializes the object properties through the expression bindings. | ||
/// </summary> | ||
/// <example> | ||
/// select new EntityType { } | ||
/// </example> | ||
/// <param name="expression">The MemberInit expression.</param> | ||
/// <returns>The MemberInit expression parameter.</returns> | ||
protected override Expression VisitMemberInit( MemberInitExpression expression ) | ||
{ | ||
for ( int i = 0; i < expression.Bindings.Count; i++ ) | ||
|
@@ -56,9 +98,22 @@ protected override Expression VisitMemberInit( MemberInitExpression expression ) | |
Statement.Append( ", " ); | ||
} | ||
|
||
if ( binding.Expression.NodeType == ExpressionType.Extension ) | ||
{ | ||
_inProjection = true; | ||
_projectionBinding = binding; | ||
} | ||
else | ||
{ | ||
_inProjection = false; | ||
} | ||
|
||
Visit( binding.Expression ); | ||
|
||
Statement.AppendFormat( " AS {0}", binding.Member.Name ); | ||
if ( binding.Expression.NodeType == ExpressionType.MemberAccess ) | ||
{ | ||
Statement.AppendFormat( " AS {0}", binding.Member.Name ); | ||
} | ||
} | ||
|
||
return expression; | ||
|
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
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
#region Copyright Notice | ||
|
||
// Copyright (c) by Achilles Software, All rights reserved. | ||
// | ||
// Licensed under the MIT License. See License.txt in the project root for license information. | ||
// | ||
// Send questions regarding this copyright notice to: mailto:[email protected] | ||
|
||
#endregion | ||
|
||
#region Namespaces | ||
|
||
using Remotion.Linq; | ||
using System.Linq; | ||
using System.Linq.Expressions; | ||
|
||
#endregion | ||
|
||
namespace Achilles.Entities.Linq | ||
{ | ||
public class FluentFetchRequest<TQueried, TFetch> : QueryableBase<TQueried> | ||
{ | ||
public FluentFetchRequest( IQueryProvider provider, Expression expression ) | ||
: base( provider, expression ) | ||
{ | ||
} | ||
} | ||
} |
Oops, something went wrong.