Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Less handler fails in conjunction with 'Microsoft.Extensions.DependencyInjection' v8 #82

Open
julealgon opened this issue Mar 6, 2024 · 5 comments

Comments

@julealgon
Copy link

The Less request handler (BundleTransformer.Less.HttpHandlers.LessAssetHandler) defines 2 public constructors:

/// <summary>
/// Constructs a instance of the debugging LESS HTTP handler
/// </summary>
public LessAssetHandler()
: this(HttpContext.Current.Cache,
BundleTransformerContext.Current.FileSystem.GetVirtualFileSystemWrapper(),
BundleTransformerContext.Current.Configuration.GetCoreSettings().AssetHandler)
{ }
/// <summary>
/// Constructs a instance of the debugging LESS HTTP handler
/// </summary>
/// <param name="cache">Server cache</param>
/// <param name="virtualFileSystemWrapper">Virtual file system wrapper</param>
/// <param name="assetHandlerConfig">Configuration settings of the debugging HTTP handle</param>
public LessAssetHandler(Cache cache,
IVirtualFileSystemWrapper virtualFileSystemWrapper,
AssetHandlerSettings assetHandlerConfig)
: base(cache, virtualFileSystemWrapper, assetHandlerConfig)
{ }

This breaks when attempting to resolve the handler through DI when using Microsoft.Extensions.DependencyInjection v8, as a breaking change was introduced that now throws an InvalidOperationException when multiple valid constructors are found.

Section of a stacktrace from our WebForms app which uses Autofac through MEDI: 

Exception information:
Exception type: InvalidOperationException
Exception message: Multiple constructors accepting all given argument types have been found in type 'BundleTransformer.Less.HttpHandlers.LessAssetHandler'. There should only be one applicable constructor.
at bool Microsoft.Extensions.DependencyInjection.ActivatorUtilities.TryFindMatchingConstructor(Type instanceType, Type[] argumentTypes, out ConstructorInfo matchingConstructor, out Nullable[] parameterMap)
at void Microsoft.Extensions.DependencyInjection.ActivatorUtilities.FindApplicableConstructor(Type instanceType, Type[] argumentTypes, out ConstructorInfo matchingConstructor, out Nullable[] matchingParameterMap)
at object Microsoft.Extensions.DependencyInjection.ActivatorUtilities.CreateInstance(IServiceProvider provider, Type instanceType, object[] parameters)
at object OutProject.DependencyInjection.AutofacWebObjectActivator.GetService(Type serviceType) in ....

And how we are configuring the handler in the web.config:

<system.web>
  <pages>
    <httpHandlers>
      <add path="*.less" verb="GET" type="BundleTransformer.Less.HttpHandlers.LessAssetHandler, BundleTransformer.Less" />
      ...
    </httpHandlers>
    ...
  </pages>
</system.web>

Here is the documentation regarding the breaking change in MEDI v8 that explains the issue in more detail:

This likely breaks with other containers or setups as well since depending on the DI adapter implementation, it could just call directly into a build-up method in the DI library and many DI libraries will choke when multiple public constructors are found.

In our setup, we should be able to workaround this issue by manually registering the handler using a factory registration, but you might still want to reconsider the design here and opt to have just a single constructor (and maybe move the second one into a factory class).

@Taritsyn
Copy link
Owner

Taritsyn commented Mar 7, 2024

Hello, Juliano!

Bundle Transformer and Microsoft.Extensions.DependencyInjection are things from different worlds. I do not recommend that you even try to create a instances of the Bundle Transformer's types through DI.

I got feeling that you are trying to use the LessAssetHandler separately from the Bundle Transformer's infrastructure. If this is case, then perhaps your best solution would be to use a dotless library.

@julealgon
Copy link
Author

Hello, Juliano!

Bundle Transformer and Microsoft.Extensions.DependencyInjection are things from different worlds. I do not recommend that you even try to create a instances of the Bundle Transformer's types through DI.

I would've agreed with you a few years ago, but not anymore. As you can see, we have a WebForms project that leverages your Less handler via standard web.config setup, and then we added dependency injection support to that project once it became available.

WebForms with DI enabled will attempt to resolve every object through the DI container, including ones defined via configuration like this.

I got feeling that you are trying to use the LessAssetHandler separately from the Bundle Transformer's infrastructure. If this is case, then perhaps your best solution would be to use a dotless library.

I'm not sure I understand what you are saying there (to be fair, I don't know the precise history of when/how this handler was added to our project, it was probably a very long time ago), but I've since resolved the problem with a modification to our implementation of the IServiceProvider by manually filtering out types with multiple constructors and skipping those in favor of the standard Activator.CreateInstance instead of relying on the containers ActivatorUtilities.CreateInstance`. I wish that wasn't necessary but it was a simple enough fix.

The only reason I decided to create this issue was to make you aware of the problem (I do consider it a problem), but if you disagree, do feel free and close the issue.

@Taritsyn
Copy link
Owner

Taritsyn commented Mar 7, 2024

WebForms with DI enabled will attempt to resolve every object through the DI container, including ones defined via configuration like this.

Could you please provide a link to the documentation or article that describes this approach in ASP.NET Web Forms.

@julealgon
Copy link
Author

WebForms with DI enabled will attempt to resolve every object through the DI container, including ones defined via configuration like this.

Could you please provide a link to the documentation or article that describes this approach in ASP.NET Web Forms.

This is a blog post from back when it was first made available. It uses the Unity container but you can use any other container (we use Autofac on our side, for instance):

And this is the relevant bits that I mentioned:

Areas that Dependency Injection can be used

There are many areas you can use Dependency Injection in WebForms applications now. Here is a complete list.

  • Pages and controls
    • WebForms page
    • User control
    • Custom control
  • IHttpHandler and IHttpHandlerFactory
  • IHttpModule
  • Providers
    • BuildProvider
    • ResourceProviderFactory
    • Health monitoring provider
    • Any ProviderBase based provider created by System.Web.Configuration.ProvidersHelper.InstantiateProvider. e.g. custom sessionstate provider

@Taritsyn
Copy link
Owner

Taritsyn commented Mar 7, 2024

Thanks for information!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants