3
3
4
4
using System ;
5
5
using System . Linq ;
6
+ using System . Linq . Expressions ;
6
7
using System . Reflection ;
7
8
using System . Threading . Tasks ;
8
9
using Microsoft . AspNet . Http ;
@@ -16,7 +17,9 @@ namespace Microsoft.AspNet.Builder
16
17
/// </summary>
17
18
public static class UseMiddlewareExtensions
18
19
{
19
- const string InvokeMethodName = "Invoke" ;
20
+ private const string InvokeMethodName = "Invoke" ;
21
+
22
+ private static readonly MethodInfo GetServiceInfo = typeof ( UseMiddlewareExtensions ) . GetMethod ( nameof ( GetService ) , BindingFlags . NonPublic | BindingFlags . Static ) ;
20
23
21
24
/// <summary>
22
25
/// Adds a middleware type to the application's request pipeline.
@@ -49,7 +52,7 @@ public static IApplicationBuilder UseMiddleware(this IApplicationBuilder app, Ty
49
52
throw new InvalidOperationException ( Resources . FormatException_UseMiddleMutlipleInvokes ( InvokeMethodName ) ) ;
50
53
}
51
54
52
- if ( invokeMethods . Length == 0 )
55
+ if ( invokeMethods . Length == 0 )
53
56
{
54
57
throw new InvalidOperationException ( Resources . FormatException_UseMiddlewareNoInvokeMethod ( InvokeMethodName ) ) ;
55
58
}
@@ -63,15 +66,20 @@ public static IApplicationBuilder UseMiddleware(this IApplicationBuilder app, Ty
63
66
var parameters = methodinfo . GetParameters ( ) ;
64
67
if ( parameters . Length == 0 || parameters [ 0 ] . ParameterType != typeof ( HttpContext ) )
65
68
{
66
- throw new InvalidOperationException ( Resources . FormatException_UseMiddlewareNoParameters ( InvokeMethodName , nameof ( HttpContext ) ) ) ;
69
+ throw new InvalidOperationException ( Resources . FormatException_UseMiddlewareNoParameters ( InvokeMethodName , nameof ( HttpContext ) ) ) ;
67
70
}
68
71
69
- var instance = ActivatorUtilities . CreateInstance ( app . ApplicationServices , middleware , new [ ] { next } . Concat ( args ) . ToArray ( ) ) ;
72
+ var ctorArgs = new object [ args . Length + 1 ] ;
73
+ ctorArgs [ 0 ] = next ;
74
+ Array . Copy ( args , 0 , ctorArgs , 1 , args . Length ) ;
75
+ var instance = ActivatorUtilities . CreateInstance ( app . ApplicationServices , middleware , ctorArgs ) ;
70
76
if ( parameters . Length == 1 )
71
77
{
72
78
return ( RequestDelegate ) methodinfo . CreateDelegate ( typeof ( RequestDelegate ) , instance ) ;
73
79
}
74
80
81
+ var factory = Compile < object > ( methodinfo , parameters ) ;
82
+
75
83
return context =>
76
84
{
77
85
var serviceProvider = context . RequestServices ?? applicationServices ;
@@ -80,20 +88,97 @@ public static IApplicationBuilder UseMiddleware(this IApplicationBuilder app, Ty
80
88
throw new InvalidOperationException ( Resources . FormatException_UseMiddlewareIServiceProviderNotAvailable ( nameof ( IServiceProvider ) ) ) ;
81
89
}
82
90
83
- var arguments = new object [ parameters . Length ] ;
84
- arguments [ 0 ] = context ;
85
- for ( var index = 1 ; index != parameters . Length ; ++ index )
86
- {
87
- var serviceType = parameters [ index ] . ParameterType ;
88
- arguments [ index ] = serviceProvider . GetService ( serviceType ) ;
89
- if ( arguments [ index ] == null )
90
- {
91
- throw new Exception ( string . Format ( "No service for type '{0}' has been registered." , serviceType ) ) ;
92
- }
93
- }
94
- return ( Task ) methodinfo . Invoke ( instance , arguments ) ;
91
+ return factory ( instance , context , serviceProvider ) ;
95
92
} ;
96
93
} ) ;
97
94
}
95
+
96
+ private static Func < T , HttpContext , IServiceProvider , Task > Compile < T > ( MethodInfo methodinfo , ParameterInfo [ ] parameters )
97
+ {
98
+
99
+ // If we call something like
100
+ //
101
+ // public class Middleware
102
+ // {
103
+ // public Task Invoke(HttpContext context, ILoggerFactory loggeryFactory)
104
+ // {
105
+ //
106
+ // }
107
+ // }
108
+ //
109
+
110
+ // We'll end up with something like this:
111
+ // Generic version:
112
+ //
113
+ // Task Invoke(Middleware instance, HttpContext httpContext, IServiceprovider provider)
114
+ // {
115
+ // return instance.Invoke(httpContext, (ILoggerFactory)UseMiddlewareExtensions.GetService(provider, typeof(ILoggerFactory));
116
+ // }
117
+
118
+ // Non generic version:
119
+ //
120
+ // Task Invoke(object instance, HttpContext httpContext, IServiceprovider provider)
121
+ // {
122
+ // return ((Middleware)instance).Invoke(httpContext, (ILoggerFactory)UseMiddlewareExtensions.GetService(provider, typeof(ILoggerFactory));
123
+ // }
124
+
125
+ // context =>
126
+ // {
127
+ // var serviceProvider = context.RequestServices ?? applicationServices;
128
+ // if (serviceProvider == null)
129
+ // {
130
+ // throw new InvalidOperationException(Resources.FormatException_UseMiddlewareIServiceProviderNotAvailable(nameof(IServiceProvider)));
131
+ // }
132
+ //
133
+ // return Invoke(httpContext, serviceProvider);
134
+ // }
135
+
136
+ var middleware = typeof ( T ) ;
137
+
138
+ var httpContextArg = Expression . Parameter ( typeof ( HttpContext ) , "httpContext" ) ;
139
+ var providerArg = Expression . Parameter ( typeof ( IServiceProvider ) , "serviceProvider" ) ;
140
+ var instanceArg = Expression . Parameter ( middleware , "middleware" ) ;
141
+
142
+ var methodArguments = new Expression [ parameters . Length ] ;
143
+ methodArguments [ 0 ] = httpContextArg ;
144
+ for ( int i = 1 ; i < parameters . Length ; i ++ )
145
+ {
146
+ var parameterType = parameters [ i ] . ParameterType ;
147
+ if ( parameterType . IsByRef )
148
+ {
149
+ throw new NotSupportedException ( Resources . FormatException_InvokeDoesNotSupportRefOrOutParams ( InvokeMethodName ) ) ;
150
+ }
151
+
152
+ var parameterTypeExpression = new Expression [ ] {
153
+ providerArg ,
154
+ Expression . Constant ( parameterType , typeof ( Type ) ) ,
155
+ Expression . Constant ( methodinfo . DeclaringType , typeof ( Type ) )
156
+ } ;
157
+ methodArguments [ i ] = Expression . Convert ( Expression . Call ( GetServiceInfo , parameterTypeExpression ) , parameterType ) ;
158
+ }
159
+
160
+ Expression middlewareInstanceArg = instanceArg ;
161
+ if ( methodinfo . DeclaringType != typeof ( T ) )
162
+ {
163
+ middlewareInstanceArg = Expression . Convert ( middlewareInstanceArg , methodinfo . DeclaringType ) ;
164
+ }
165
+
166
+ var body = Expression . Call ( middlewareInstanceArg , methodinfo , methodArguments ) ;
167
+
168
+ var lambda = Expression . Lambda < Func < T , HttpContext , IServiceProvider , Task > > ( body , instanceArg , httpContextArg , providerArg ) ;
169
+
170
+ return lambda . Compile ( ) ;
171
+ }
172
+
173
+ private static object GetService ( IServiceProvider sp , Type type , Type middleware )
174
+ {
175
+ var service = sp . GetService ( type ) ;
176
+ if ( service == null )
177
+ {
178
+ throw new InvalidOperationException ( Resources . FormatException_InvokeMiddlewareNoService ( type , middleware ) ) ;
179
+ }
180
+
181
+ return service ;
182
+ }
98
183
}
99
184
}
0 commit comments