Ildar
Ildar

Reputation: 115

Constructor injection not working in .NET 4.7.2 Dependency Injection in ASP.NET WebForms

I used article "Use Dependency Injection In WebForms Application" https://devblogs.microsoft.com/aspnet/use-dependency-injection-in-webforms-application/ The project retargeted to .NET Framework 4.7.2 in project properties and in web.config:

    <system.web>
    <httpRuntime targetFramework="4.72" ...

AspNet.WebFormsDependencyInjection.Unity NuGet package is installed.
Type is registered in Global:

    public class Global : System.Web.HttpApplication
    {
        void Application_Start(object sender, EventArgs e)
        {
            var container = this.AddUnity();
            container.RegisterType<IVCRole, eVCRole>();
        }
        ...

I checked container and it is working and registering interface IVCRole mapping to class eVCRole. Default.aspx.cs is refactored:

  public partial class Default : System.Web.UI.Page
  {
    private IVCRole vcr; 
    public Default(IVCRole avcr) 
    { 
      vcr = avcr;
    }   
    protected void Page_Load(object sender, EventArgs e)
    {
    ...

But when I run web application there is an error "Constructor on type 'ASP.default_aspx' not found." If I add this constructor:

  public partial class Default : System.Web.UI.Page
  {
    private IVCRole vcr;
    public Default() {}
    public Default(IVCRole avcr)
    { 
      vcr = avcr;
    }   
    protected void Page_Load(object sender, EventArgs e)
    {
    ...

the constructor for DI

    public Default(IVCRole avcr) 
    { 
      vcr = avcr;
    }

is never called and "vcr" is always null in Page_Load. There is an article: "Dependency Injection in ASP.NET Web Forms": https://makingloops.com/dependency-injection-in-web-forms/ where this error is mentioned: "On occasion you may see a build error complaining about the lack of a zero-argument constructor on the page. I notice that this error will magically go away depending on the context. Someone else suggested using property injection with the Dependency attribute on pages to get around this, but i didn’t find that was necessary." But in my case there is no "magic". There is similar question in Stackoverflow: .NET 4.7.2 Dependency Injection in ASP.NET WebForms Website - Constructor injection not working But in my case property injection is not working:

public partial class Default : System.Web.UI.Page
{
    [Dependency]
    public IVCRole vcr { get; set; }    

    protected void Page_Load(object sender, EventArgs e)
    {
    ...

"vcr" in Page_Load is still null. There is solution to get it working with custom implementation of DI provider but I already using .NET 4.7.2 an Unity. Author mentioned that for web application should not be any problem as the problem is with website compiler. How to get DI constructor or property injection to working in Default page using .NET 4.7.2 and Unity?

This is Stack:

[MissingMethodException: Constructor on type 'ASP.default_aspx' not found.]
   System.RuntimeType.CreateInstanceImpl(BindingFlags bindingAttr, Binder binder, Object[] args, CultureInfo culture, Object[] activationAttributes, StackCrawlMark& stackMark) +1173
   System.Activator.CreateInstance(Type type, BindingFlags bindingAttr, Binder binder, Object[] args, CultureInfo culture, Object[] activationAttributes) +130
   System.Activator.CreateInstance(Type type, BindingFlags bindingAttr, Binder binder, Object[] args, CultureInfo culture) +21
   Microsoft.AspNet.WebFormsDependencyInjection.Unity.ContainerServiceProvider.DefaultCreateInstance(Type type) +17
   Microsoft.AspNet.WebFormsDependencyInjection.Unity.ContainerServiceProvider.GetService(Type serviceType) +161
   __ASP.FastObjectFactory_app_web_mmaneivx.Create_ASP_default_aspx() in c:\Windows\Microsoft.NET\Framework\v4.0.30319\Temporary ASP.NET Files\vs\19e4d468\8c7800a0\App_Web_mmaneivx.2.cs:0
   System.Web.Compilation.BuildResultCompiledType.CreateInstance() +31
   System.Web.Compilation.BuildManager.CreateInstanceFromVirtualPath(VirtualPath virtualPath, Type requiredBaseType, HttpContext context, Boolean allowCrossApp) +104
   System.Web.UI.PageHandlerFactory.GetHandlerHelper(HttpContext context, String requestType, VirtualPath virtualPath, String physicalPath) +33
   System.Web.UI.PageHandlerFactory.GetHandler(HttpContext context, String requestType, String virtualPath, String path) +39
   System.Web.MaterializeHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +386
   System.Web.HttpApplication.ExecuteStepImpl(IExecutionStep step) +50
   System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +163

Upvotes: 3

Views: 7097

Answers (4)

LazyDog
LazyDog

Reputation: 357

I had a very similar issue as the above answer by Света Нестерова https://stackoverflow.com/a/65205408/6901318

One of the dependencies I was trying to register was from an internally maintained Nuget package.

For whatever reason, it is not able to register the type when it is defined in a dll like that. I have done this with .Net Core and it works fine. I am assuming that those references have not been resolved at this point in the application startup. I could be wrong though. It is just a guess, if anyone knows please comment on it.

I ended up making an empty class that inherited from the class I wanted to inject. And, that also implemented the interface that was defined in the NuGet package.

public interface IADAuthenticationManager: IADManager
{

}

IADAuthenticationManager is the wrapper interface and IADManager is the interface defined in the package.

public class ADAuthenticationManager: ADManager, IADAuthenticationManager
{
    public ADAuthenticationManager(IADProvider aDProvider, IErrorLogger logger) : 
    base(aDProvider, logger)
    {
    }
}

ADAuthenticationManager is the wrapper class. ADManager is the class that implements IADManager and is also defined in the NuGet package. IADAuthenticationManager is the wrapper interface from above.

In my Global.asax.cs

protected void Application_Start(object sender, EventArgs e)
    {
        ...            
        var container = this.AddUnity();
        ...
        container.RegisterType<IADManager, ADAuthenticationManager>();
        container.RegisterType<IADAuthenticationManager, ADAuthenticationManager>();
    }

The classes in the NuGet package also had some dependencies that needed wrapper classes created as well. It was a process. But, everything is working now.

Upvotes: 0

ThomasH
ThomasH

Reputation: 526

For me the solution was to remove optimizeCompilations="true" from:

<system.web>
    <compilation debug="true" targetFramework="4.8" optimizeCompilations="true">
    </compilation>
</system.web>

in Web.Config

Upvotes: 1

I had exactly the same problem (exception MissingMethodException: Constructor on type 'ASP.*my_page*_aspx' not found.) and it turned out that there was error in creating inner dependency.

I mean, I had:

public class Global :  System.Web.HttpApplication
{
    private IUnityContainer container;

    protected void Application_Start(object sender, EventArgs e)
    {
        container = this.AddUnity();

        container.RegisterType<IDataStorage>(
            new InjectionFactory(c => DataStorageBuilder.GetDefaultStorage()));

        RouteConfig.RegisterRoutes(RouteTable.Routes);
        BundleConfig.RegisterBundles(BundleTable.Bundles);
    }
}

and Page:

public partial class Contact : System.Web.UI.Page
{
    private IDataStorage storage { get; set; }

    public Contact(IDataStorage storage)
    {
        this.storage = storage;
    }

    protected void Page_Load(object sender, EventArgs e)
    {
        storage.DoRequest();
    }
}

And DataStorageBuilder.GetDefaultStorage() actually threw exception, which Unity wrapped in Constructor on type 'ASP.contact_aspx' not found.

So maybe, you need to check that container can correctly create all inner dependencies of page (in your example it is eVCRole).

Upvotes: 2

Dai
Dai

Reputation: 155250

Disclaimer: I maintain this NuGet package and project that use Microsoft.Extensions.DependencyInjection for ASP.NET WebForms (and MVC, SignalR and WCF) in the .NET Framework 4.7.2 - however the content of this post isn't specific to my implementation of DI for ASP.NET.


  • Check your .csproj:

    • Ensure you're targeting .NET Framework 4.7.2 or later (note that many shared web-hosts - including Azure App Services - may be running older versions of the .NET Framework):

      <TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
      
  • Check your web.config

    • Ensure you have <system.web><compilation targetFramework="4.7.2">
    • Ensure you have <system.web><httpRuntime targetFramework="4.7.2"/>
    • If you have <assemblies><clear /> ensure you have <add assembly="*"/> after the <clear /> or otherwise ensure you're explicitly listing all required assemblies in <add /> elements.
  • Your ConfigureServices method (or anything that configures DI) must run before Global.asax's Application_Start!

    • Currently your code is instantiating the Unity container as a local inside Application_Start - this is a bad idea (as you aren't preserving a strong-reference in a field - a bug elsewhere could cause the GC to collect your DI container, which would be a bad thing.

    • You also need to add a IHttpModule to support scoped DI containers (my package does this for you already).

    • This is done by using the PreApplicationStartMethod attribute (from WebActivatorEx.

      • Note that PreApplicationStartMethod is not the same as OWIN's [assembly: Microsoft.Owin.OwinStartup() attribute! Your project may need to use both if you're using DI with SignalR.
      • Your Startup class code should look like this:
      [assembly: PreApplicationStartMethod ( typeof( global::MyProject.RssStartup ), methodName: nameof( global::MyProject.MyStartup.OnPreStart ) )]
      
      namespace MyProject
      {
          internal static class MyStartup
          {
              internal static void OnPreStart()
              {
                  // Set-up your DI system here and then call your `ConfigureServices` method before this method returns.
              }
          }
      }
      

Upvotes: 5

Related Questions