Skip to content

Commit c076a60

Browse files
committed
2 parents 7b80a00 + ddb8bb2 commit c076a60

File tree

1 file changed

+35
-35
lines changed

1 file changed

+35
-35
lines changed

src/SimpleInjector.Integration.AspNetCore/SimpleInjectorAspNetIntegrationExtensions.cs

Lines changed: 35 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -76,30 +76,20 @@ public static T GetRequiredRequestService<T>(this IApplicationBuilder builder)
7676
/// <param name="container">The container to resolve <typeparamref name="TMiddleware"/> from.</param>
7777
/// <returns>The supplied <see cref="IApplicationBuilder"/> instance.</returns>
7878
/// <exception cref="ArgumentNullException">Thrown when one of the arguments is a null reference.</exception>
79+
/// <exception cref="ArgumentException">Thrown when the <typeparamref name="TMiddleware"/> does not
80+
/// implement <see cref="IMiddleware"/> or not a concrete constructable
81+
/// type.</exception>
7982
public static IApplicationBuilder UseMiddleware<TMiddleware>(
8083
this IApplicationBuilder app, Container container)
81-
where TMiddleware : class, IMiddleware
8284
{
83-
Requires.IsNotNull(app, nameof(app));
84-
Requires.IsNotNull(container, nameof(container));
85-
86-
var lifestyle = container.Options.LifestyleSelectionBehavior.SelectLifestyle(typeof(TMiddleware));
87-
88-
// By creating an InstanceProducer up front, it will be known to the container, and will be part
89-
// of the verification process of the container.
90-
// Note that the middleware can't be registered in the container, because at this point the
91-
// container might already be locked (which will happen when the new ASP.NET Core 3 Host class is
92-
// used).
93-
InstanceProducer<IMiddleware> producer =
94-
lifestyle.CreateProducer<IMiddleware, TMiddleware>(container);
95-
96-
app.Use((c, next) =>
97-
{
98-
IMiddleware middleware = producer.GetInstance();
99-
return middleware.InvokeAsync(c, _ => next());
100-
});
101-
102-
return app;
85+
// DESIGN: The "where TMiddleware : class, IMiddleware" generic type constraint was deliberately removed
86+
// from this generic method. In case a user calls app.UserMiddleware<T>(container) on a T that didn't
87+
// implement IMiddleware, C# would call one of ASP.NET Core's built-in extension method (i.e.
88+
// UseMiddlewareExtensions.UseMiddleware<T>(IApplicationBuilder, object[])) instead of giving a compile
89+
// error. This would result in a really confusing error message. By removing the constraint, C# will still
90+
// prefer calling this method, in which case we can throw an expressive exception explaining that
91+
// TMiddleware should implement IMiddleware. See #5
92+
return UseMiddlewareInternal(app, typeof(TMiddleware), container, nameof(TMiddleware));
10393
}
10494

10595
/// <summary>
@@ -114,30 +104,40 @@ public static IApplicationBuilder UseMiddleware<TMiddleware>(
114104
/// <returns>The supplied <see cref="IApplicationBuilder"/> instance.</returns>
115105
/// <exception cref="ArgumentNullException">Thrown when one of the arguments is a null reference.</exception>
116106
/// <exception cref="ArgumentException">Thrown when the <paramref name="middlewareType"/> does not
117-
/// derive from <see cref="IMiddleware"/>, is an open-generic type, or not a concrete constructable
118-
/// type.</exception>
107+
/// implement <see cref="IMiddleware"/>, is open-generic, or not a concrete constructable type.</exception>
119108
public static IApplicationBuilder UseMiddleware(
120109
this IApplicationBuilder app, Type middlewareType, Container container)
110+
{
111+
return UseMiddlewareInternal(app, middlewareType, container, nameof(middlewareType));
112+
}
113+
114+
private static IApplicationBuilder UseMiddlewareInternal(
115+
IApplicationBuilder app, Type middlewareType, Container container, string middlewareTypeArgName)
121116
{
122117
Requires.IsNotNull(app, nameof(app));
123-
Requires.IsNotNull(middlewareType, nameof(middlewareType));
118+
Requires.IsNotNull(middlewareType, middlewareTypeArgName);
124119
Requires.IsNotNull(container, nameof(container));
125120

126-
Requires.ServiceIsAssignableFromImplementation(
127-
typeof(IMiddleware), middlewareType, nameof(middlewareType));
121+
Requires.ServiceIsAssignableFromImplementation(typeof(IMiddleware), middlewareType, middlewareTypeArgName);
128122

129-
Requires.IsNotOpenGenericType(middlewareType, nameof(middlewareType));
123+
Requires.IsNotOpenGenericType(middlewareType, middlewareTypeArgName);
130124

131125
var lifestyle = container.Options.LifestyleSelectionBehavior.SelectLifestyle(middlewareType);
132126

133-
// By creating an InstanceProducer up front, it will be known to the container, and will be part
134-
// of the verification process of the container.
135-
// Note that the middleware can't be registered in the container, because at this point the
136-
// container might already be locked (which will happen when the new ASP.NET Core 3 Host class is
137-
// used).
138-
InstanceProducer<IMiddleware> producer =
139-
lifestyle.CreateProducer<IMiddleware>(middlewareType, container);
140-
127+
// By creating an InstanceProducer up front, it will be known to the container, and will be part of the
128+
// verification process of the container (in case verification is done after a call to UseMiddleware).
129+
// NOTE: the middleware can't be registered in the container using container.Register, because:
130+
// 1. at this point the container might already be locked (which will happen when the new ASP.NET Core 3
131+
// Host class is used).
132+
// 2. This method might be used in a map, e.g. app.Map("/api/abc", b => b.UseMiddleware<M>(container)).
133+
// When there are multiple mappings to the same middleware type M, the second call would fail.
134+
InstanceProducer<IMiddleware> producer = lifestyle.CreateProducer<IMiddleware>(middlewareType, container);
135+
136+
// NOTE: CreateProducer will deduplicate the underlying Registration for middlewareType, which means its
137+
// lifestyle will be respected in case there are multiple maps to the same type. But in case decorators are
138+
// applied to IMiddleware, each map gets its own decorator (even if the decorator is registered as
139+
// singleton). This isn't per se the best design, but keep this in mind when changing this; users might be
140+
// depending on this behavior.
141141
app.Use((c, next) =>
142142
{
143143
IMiddleware middleware = producer.GetInstance();

0 commit comments

Comments
 (0)