@@ -33,14 +33,97 @@ public IQueryable<TEntity> CreateQuery<TEntity>(Expression expression)
3333 return new SubSonicCollection < TEntity > ( this , BuildQuery ( expression ) ) ;
3434 }
3535
36+ public TResult ExecuteMethod < TResult > ( MethodCallExpression call )
37+ {
38+ if ( call is null )
39+ {
40+ throw Error . ArgumentNull ( nameof ( call ) ) ;
41+ }
42+
43+ DbSelectExpression dbSelect = null ;
44+ Expression where = null ;
45+
46+ for ( int i = 0 , n = call . Arguments . Count ; i < n ; i ++ )
47+ {
48+ if ( call . Arguments [ i ] is DbSelectExpression select )
49+ {
50+ dbSelect = select ;
51+ }
52+ else if ( call . Arguments [ i ] is UnaryExpression unary )
53+ {
54+ if ( unary . Operand is LambdaExpression lambda )
55+ {
56+ where = BuildWhere ( dbSelect , lambda ) ;
57+ }
58+ }
59+ }
60+
61+ if ( call . Method . Name . In ( nameof ( Queryable . Single ) , nameof ( Queryable . SingleOrDefault ) , nameof ( Queryable . First ) , nameof ( Queryable . FirstOrDefault ) ) )
62+ {
63+ object result = Execute < TResult > ( BuildSelect ( dbSelect , where ) ) ;
64+
65+ if ( result is TResult matched )
66+ {
67+ return matched ;
68+ }
69+ else if ( result is IEnumerable < TResult > enumerable )
70+ {
71+ return enumerable . Any ( ) ? enumerable . ElementAt ( 0 ) : default ( TResult ) ;
72+ }
73+ #if NETSTANDARD2_0
74+ else if ( call . Method . Name . Contains ( "Default" ) )
75+ #elif NETSTANDARD2_1
76+ else if ( call . Method . Name . Contains ( "Default" , StringComparison . CurrentCulture ) )
77+ #endif
78+ {
79+ return default ( TResult ) ;
80+ }
81+ else
82+ {
83+ throw Error . InvalidOperation ( $ "Method { call . Method . Name } expects data.") ;
84+ }
85+ }
86+ else if ( call . Method . Name . In ( nameof ( Queryable . Count ) , nameof ( Queryable . LongCount ) ) )
87+ {
88+ if ( BuildSelect ( dbSelect , where ) is DbSelectExpression select )
89+ {
90+ if ( ! Enum . TryParse < AggregateType > ( call . Method . Name , out AggregateType aggregateType ) )
91+ {
92+ throw Error . NotSupported ( SubSonicErrorMessages . MethodNotSupported . Format ( call . Method . Name ) ) ;
93+ }
94+
95+ TResult result = Execute < TResult > ( DbExpression . DbSelectAggregate ( select , new [ ]
96+ {
97+ DbExpression . DbAggregate ( typeof ( TResult ) , aggregateType , select . Columns . First ( x => x . Property . IsPrimaryKey ) . Expression )
98+ } ) ) ;
99+
100+ if ( select . Take is ConstantExpression take )
101+ {
102+ if ( result . IsIntGreaterThan ( take . Value ) )
103+ {
104+ return ( TResult ) Convert . ChangeType ( take . Value , typeof ( TResult ) , CultureInfo . InvariantCulture ) ;
105+ }
106+ }
107+
108+ return result ;
109+ }
110+ }
111+
112+ throw Error . NotSupported ( SubSonicErrorMessages . ExpressionNotSupported . Format ( call . Method . Name ) ) ;
113+ }
114+
36115 public TResult Execute < TResult > ( Expression expression )
37116 {
38117 if ( expression is null )
39118 {
40119 throw new ArgumentNullException ( nameof ( expression ) ) ;
41120 }
42121
43- if ( expression is DbExpression query )
122+ if ( expression is MethodCallExpression call )
123+ { // execution request originates from the System.Linq namespace
124+ return ExecuteMethod < TResult > ( call ) ;
125+ }
126+ else if ( expression is DbExpression query )
44127 { // execution request is from the subsonic namespace
45128 using ( SharedDbConnectionScope Scope = DbContext . ServiceProvider . GetService < SharedDbConnectionScope > ( ) )
46129 {
@@ -97,75 +180,6 @@ public TResult Execute<TResult>(Expression expression)
97180 }
98181 }
99182 }
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 ++ )
107- {
108- if ( call . Arguments [ i ] is DbSelectExpression select )
109- {
110- dbSelect = select ;
111- }
112- else if ( call . Arguments [ i ] is UnaryExpression unary )
113- {
114- if ( unary . Operand is LambdaExpression lambda )
115- {
116- where = BuildWhere ( dbSelect , lambda ) ;
117- }
118- }
119- }
120-
121- if ( call . Method . Name . In ( nameof ( Queryable . Single ) , nameof ( Queryable . SingleOrDefault ) , nameof ( Queryable . First ) , nameof ( Queryable . FirstOrDefault ) ) )
122- {
123- object result = Execute < TResult > ( BuildSelect ( dbSelect , where ) ) ;
124-
125- if ( result is TResult matched )
126- {
127- return matched ;
128- }
129- else if ( result is IEnumerable < TResult > enumerable )
130- {
131- return enumerable . Any ( ) ? enumerable . ElementAt ( 0 ) : default ( TResult ) ;
132- }
133- #if NETSTANDARD2_0
134- else if ( call . Method . Name . Contains ( "Default" ) )
135- #elif NETSTANDARD2_1
136- else if ( call . Method . Name . Contains ( "Default" , StringComparison . CurrentCulture ) )
137- #endif
138- {
139- return default ( TResult ) ;
140- }
141- else
142- {
143- throw Error . InvalidOperation ( $ "Method { call . Method . Name } expects data.") ;
144- }
145- }
146- else if ( call . Method . Name . In ( nameof ( Queryable . Count ) ) )
147- {
148- if ( BuildSelect ( dbSelect , where ) is DbSelectExpression select )
149- {
150- TResult result = Execute < TResult > ( DbExpression . DbSelectAggregate ( select , new [ ]
151- {
152- DbExpression . DbAggregate ( typeof ( TResult ) , AggregateType . Count , select . Columns . First ( x => x . Property . IsPrimaryKey ) . Expression )
153- } ) ) ;
154-
155- if ( select . Take is ConstantExpression take )
156- {
157- if ( result . IsIntGreaterThan ( take . Value ) )
158- {
159- return ( TResult ) Convert . ChangeType ( take . Value , typeof ( TResult ) , CultureInfo . InvariantCulture ) ;
160- }
161- }
162-
163- return result ;
164- }
165- }
166-
167- throw Error . NotSupported ( SubSonicErrorMessages . ExpressionNotSupported . Format ( call . Method . Name ) ) ;
168- }
169183
170184 throw new NotSupportedException ( expression . ToString ( ) ) ;
171185 }
0 commit comments