13

We are currently working with an older project (ASP.NET Web Forms Website) and trying to see if we can set up dependency injection for it.

Need to emphasize: this is NOT a Web Application project... it's the older type, the Website.

It is currently targeting .NET 4.7.2:

<httpRuntime targetFramework="4.7.2" />

So far, we've included the NuGet package:

<package id="Microsoft.AspNet.WebFormsDependencyInjection.Unity" version="1.0.0" targetFramework="net472" />

Defined some dummy interface and implementations:

public interface IDependencyTest
{
    string GetName();
}

public class DependencyTest : IDependencyTest
{
    public string GetName()
    {
        return "Mwuhahaha!!!";
    }
}

And wired the DI container in the Application_Start event handler in global.asax:

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

    container.RegisterType<IDependencyTest, DependencyTest>();
}

Required namespaces were imported:

<%@ Import Namespace="Microsoft.AspNet.WebFormsDependencyInjection.Unity" %>
<%@ Import Namespace="Unity" %>

Created a test page Teste.aspx:

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Teste.aspx.cs" Inherits="Teste" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
</head>
<body>
    <form id="form1" runat="server">
    <div>    
        <asp:Label ID="lblDisplay" runat="server" Text="No luck..."></asp:Label>    
    </div>
    </form>
</body>
</html>

With the following code behind:

public partial class Teste : System.Web.UI.Page
{
    private IDependencyTest _dependencyTest;

    public Teste(IDependencyTest dependencyTest)
    {
        _dependencyTest = dependencyTest;
    }

    protected void Page_Load(object sender, EventArgs e)
    {
        lblDisplay.Text = _dependencyTest.GetName();
    }    
}

All this setup will fail with the following exception:

C:\Windows\Microsoft.NET\Framework64\v4.0.30319\Temporary ASP.NET Files\root\7a04dd72\81815e95\App_Web_teste.aspx.cdcab7d2.rtms4_ja.0.cs(187): error CS7036: There is no argument given that corresponds to the required formal parameter 'dependencyTest' of 'Teste.Teste(IDependencyTest)'

However, property injection does work:

using Unity.Attributes;

public partial class Teste : System.Web.UI.Page
{
    [Dependency]
    public IDependencyTest DependencyTest { get; set; }    

    protected void Page_Load(object sender, EventArgs e)
    {
        lblDisplay.Text = DependencyTest.GetName();
    }    
}

enter image description here

To be honest, I'd really like to use the constructor injection...

Why isn't it working with our current setup?

Is it because it's a ASP.NET Website and NOT an ASP.NET WebApplication?

Can anything be done to get it working here, also?

Mihai Caracostea
  • 8,336
  • 4
  • 27
  • 46
  • 1
    Same issue is described here: https://makingloops.com/dependency-injection-in-web-forms/ (last paragraph). I'd try deleting ``bin`` and ``obj`` folders – Piotr L Dec 04 '19 at 14:27
  • @mason You mean this? <%@ Import Namespace="Unity" %> That's in global.asax. – Mihai Caracostea Dec 04 '19 at 15:02
  • @mason Because there are already other includes made in the same fashion and I preferred to maintain the style consistent. It's not something to be bothered about. – Mihai Caracostea Dec 04 '19 at 15:27
  • @PiotrŁazarczyk I've already seen that paragraph, but unfortunately, I don't think that my error is really the same thing. Although... I did a full delete of the Temporary ASP.NET files for that site, which forced a full, clean recompilation. – Mihai Caracostea Dec 04 '19 at 15:29
  • 1
    Check https://stackoverflow.com/a/3849338/5233410 – Nkosi Dec 12 '19 at 14:23
  • @Nkosi You mean the .NET 4.7.2 solution or the old one with the custom page handler factory? – Mihai Caracostea Dec 12 '19 at 14:54
  • So you will need to add an httphandler to the pipeline which can then do the extra work for dependency injection using your preferred DI package. Basically writing a custom PageHandlerFactory. – tubakaya Dec 12 '19 at 23:06
  • I know this is old, but for me I had similar issue on visual studio with IIS Express. Had to nuke the express temp files and then also noticed although the project was a 4.7.2 app, the web.config only had: `` and `` targetFramework="4.7.2" had to be added to the `httpRuntime`tag – kenam Dec 18 '20 at 16:25
  • @MihaiCaracostea: I know this is a bit late, but I'd question the decision to keep it as a web site and not a web application, I've done the change multiple times, and it generally takes a less than a day. – jmoreno Dec 20 '21 at 21:20

1 Answers1

11

Why isn't it working with our current setup?

The default configuration of Asp.Net WebForms the System.Web.UI.Page requires a parameter less constructor to instantiate your page and start the Page Life Cycle.

Thats the reason you get this error when try to use Constructor Dependency Injection.

error CS7036: There is no argument given that corresponds to the required formal parameter 'dependencyTest' of 'Teste.Teste(IDependencyTest)'

Can anything be done to get it working here, also?

Dependency injection (DI) with Asp.Net WebForms wasn't very commom, on the gold times of WebForms, so is very difficult to find something in the official documentation.

After some searches on StackOverflow and Google I found some useful information that can help you to solve your problem.


DI wans't very well supported in Asp.Net WebForms until version 4.7.2

So if you are using an older version than 4.7.2, and can't update to a newer version, you will need to use PageHandlerFactory. As I never tried this I prefer to give you the links to the two references I found.

Why does everyone say dependency injection in ASP.NET webforms is hard when PageHandlerFactory and IHttpHandlerFactory exist?

Dependency Injection in ASP.NET Web Forms


If you a using a version newer then 4.7.2, or if you can change to this version. You can use WebForms with a better support for DI.

Step 1 – Implement IServiceProvider. You can implement your own DI logic in it or plug in another DI framework, e.g. Unity, Ninject. The following example demonstrates injecting an ILog object through the constructor.

public class SimpleActivator : IServiceProvider
{
    public object GetService(Type serviceType)
    {
        var ctors = serviceType.GetConstructors();
        ConstructorInfo targetCtor = null;
        foreach (var c in ctors)
        {
            var parameters = c.GetParameters();
            if (parameters.Count() == 1 && parameters[0].ParameterType == typeof(ILog))
            {
                targetCtor = c;
                break;
            }
        }

        if(targetCtor != null)
        {
            return targetCtor.Invoke(new object[] { new DebuggingLoger() });
        }
        else
        {
            return Activator.CreateInstance(
            serviceType,
            BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.CreateInstance,
            null,
            null,
            null);
        }
    }
}

Step 2 – Set WebObjectActivator in Global.asax.

public class Global : System.Web.HttpApplication
{        
    public override void Init()
    {
        HttpRuntime.WebObjectActivator = new SimpleActivator();

        base.Init();
    }
}

Step 3 – Use Dependency Injection in your Webform page.

public partial class WebForm2 : System.Web.UI.Page
{
    private ILog _log;

    public WebForm2(ILog l)
    {
        _log = l;
    }
}

You can see this examples, and more inflortion about it in this link: https://devblogs.microsoft.com/dotnet/announcing-the-net-framework-4-7-2/


In my opinion if you can move to a newer version of .NetFramework you should try to implement the IServiceProvider. If you can't then you should analyse if the implementation of PageHandlerFactory will save you more time in the future than the time you will expend to implement it. If it will not save you any time in the future you can keep with [Dependency] property injection.

Lutti Coelho
  • 2,134
  • 15
  • 31
  • We are already on 4.7.2 and have set up Unity as per that same blog. I have managed to get it working by some "hacking" means. I've moved the aspx.cs file to App_Code amd changed "CodeFile" to "CodeBehind" in the .aspx file (so that it gets compiled with the rest of the application code instead of the normal page compilation) and the constructor injection worked. So the issue seems to more related to the way page classes get compiled in a website project, rather different than with the web application project. – Mihai Caracostea Dec 16 '19 at 15:14
  • Of course, you can imagine that was just an experiment and is not a feasible solution. Until we get a real solution, we're working with property injection, which works properly, but it's not quite what we were hoping to achieve. – Mihai Caracostea Dec 16 '19 at 15:16
  • The reason of the error CS7030 is because of the first thing I said in the answer: The default configuration of Asp.Net WebForms the System.Web.UI.Page requires a parameter less constructor to instantiate your page and start the Page Life Cycle. – Lutti Coelho Dec 16 '19 at 15:18
  • If you already on 4.7.2 I suggest to implement the IServiceProvider, that is a feasible and real solution. – Lutti Coelho Dec 16 '19 at 15:19
  • This is pretty much what the Microsoft.AspNet.WebFormsDependencyInjection.Unity nuget package already does and it works very well for Web Application projects, but not for the old Website projects. https://github.com/aspnet/AspNetWebFormsDependencyInjection – Mihai Caracostea Dec 19 '19 at 07:53
  • 1
    I've awarded the bounty for you answer, for your effort and great documenting. However, I cannot mark it as Accepted because it does not address the issue in the question (specific to legacy ASP.NET Website projects). – Mihai Caracostea Dec 19 '19 at 07:57