@@ -14,6 +14,7 @@ namespace SubSonic.Infrastructure.Builders
1414 using Logging ;
1515 using Linq . Expressions ;
1616 using System . Threading . Tasks ;
17+ using SubSonic . Linq ;
1718
1819 public partial class DbSqlQueryBuilder
1920 {
@@ -40,8 +41,8 @@ public TResult Execute<TResult>(Expression expression)
4041 throw new ArgumentNullException ( nameof ( expression ) ) ;
4142 }
4243
43- if ( expression is DbExpression select )
44- {
44+ if ( expression is DbExpression dbExpression )
45+ { // execution request is from the subsonic namespace
4546 using ( SharedDbConnectionScope Scope = DbContext . ServiceProvider . GetService < SharedDbConnectionScope > ( ) )
4647 {
4748 CmdBehavior = typeof ( TResult ) . IsEnumerable ( ) ? CommandBehavior . Default : CommandBehavior . SingleRow ;
@@ -51,9 +52,9 @@ public TResult Execute<TResult>(Expression expression)
5152 bool isEntityModel = DbContext . DbModel . IsEntityModelRegistered ( elementType ) ;
5253
5354 if ( ! isEntityModel ||
54- DbContext . Current . ChangeTracking . Count ( elementType , select ) == 0 )
55+ DbContext . Current . ChangeTracking . Count ( elementType , dbExpression ) == 0 )
5556 {
56- IDbQuery dbQuery = ToQuery ( select ) ;
57+ IDbQuery dbQuery = ToQuery ( dbExpression ) ;
5758
5859 try
5960 {
@@ -96,47 +97,61 @@ public TResult Execute<TResult>(Expression expression)
9697 }
9798 }
9899 }
99- else if ( expression is MethodCallExpression method )
100- { // Linq call is coming from System.Linq namespace directly.
101- // expression needs to be rebuilt into something the DAL can use
102- while ( method . Arguments [ 0 ] is MethodCallExpression _method )
100+ else if ( expression is MethodCallExpression call )
101+ { // execution request originates from the System.Linq namespace
102+
103+ DbSelectExpression dbSelect = null ;
104+ Expression where = null ;
105+
106+ for ( int i = 0 , n = call . Arguments . Count ; i < n ; i ++ )
103107 {
104- method = _method ;
108+ if ( call . Arguments [ i ] is DbSelectExpression select )
109+ {
110+ dbSelect = select ;
111+ }
112+ else if ( call . Arguments [ i ] is UnaryExpression unary )
113+ {
114+ where = BuildWhere ( dbSelect . From , dbSelect . Where , dbSelect . Type , unary ) ;
115+ }
105116 }
106117
107-
108-
109- if ( method . Arguments [ 0 ] is DbSelectExpression _select )
118+ if ( call . Method . Name . In ( nameof ( Queryable . Single ) , nameof ( Queryable . SingleOrDefault ) , nameof ( Queryable . First ) , nameof ( Queryable . FirstOrDefault ) ) )
110119 {
111- Expression where = null ;
120+ object result = Execute < TResult > ( BuildSelect ( dbSelect , where ) ) ;
112121
113- for ( int i = 1 , n = method . Arguments . Count ; i < n ; i ++ )
122+ if ( result is TResult matched )
114123 {
115- where = BuildWhere ( _select . From , where , _select . Type , method . Arguments [ i ] ) ;
124+ return matched ;
116125 }
117-
118- if ( method . Method . Name . Equals ( nameof ( Queryable . Count ) , StringComparison . CurrentCulture ) )
119- { // the method count has been called on the collection
120- if ( BuildSelect ( _select , where ) is DbSelectExpression __select )
121- {
122- return Execute < TResult > ( DbExpression . DbSelectAggregate ( __select , new [ ]
123- {
124- DbExpression . DbAggregate ( typeof ( TResult ) , AggregateType . Count , __select . Columns . First ( x => x . Property . IsPrimaryKey ) . Expression )
125- } ) ) ;
126- }
126+ else if ( result is IEnumerable < TResult > enumerable )
127+ {
128+ return enumerable . Any ( ) ? enumerable . ElementAt ( 0 ) : default ( TResult ) ;
129+ }
130+ #if NETSTANDARD2_0
131+ else if ( call . Method . Name . Contains ( "Default" ) )
132+ #elif NETSTANDARD2_1
133+ else if ( call . Method . Name . Contains ( "Default" , StringComparison . CurrentCulture ) )
134+ #endif
135+ {
136+ return default ( TResult ) ;
127137 }
128138 else
129139 {
130- if ( method . Arguments . Count > 1 )
131- {
132- return Execute < TResult > ( BuildSelect ( _select , where ) ) ;
133- }
134- else
140+ throw Error . InvalidOperation ( $ "Method { call . Method . Name } expects data.") ;
141+ }
142+ }
143+ else if ( call . Method . Name . In ( nameof ( Queryable . Count ) ) )
144+ {
145+ if ( BuildSelect ( dbSelect , where ) is DbSelectExpression select )
146+ {
147+ return Execute < TResult > ( DbExpression . DbSelectAggregate ( select , new [ ]
135148 {
136- return Execute < TResult > ( _select ) ;
137- }
149+ DbExpression . DbAggregate ( typeof ( TResult ) , AggregateType . Count , select . Columns . First ( x => x . Property . IsPrimaryKey ) . Expression )
150+ } ) ) ;
138151 }
139152 }
153+
154+ throw Error . NotSupported ( SubSonicErrorMessages . ExpressionNotSupported . Format ( call . Method . Name ) ) ;
140155 }
141156
142157 throw new NotSupportedException( expression . ToString ( ) ) ;
0 commit comments