question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Setter Injection not working (Core 3.0)

See original GitHub issue

Create a default .Net core 3.0 web app

Nuget Lamar 4.0.0 Nuget Lamar.Microsoft.DependencyInjection 4.0.2

Program:

    public class Program
    {
        public static void Main(string[] args)
        {
            CreateHostBuilder(args).Build().Run();
        }

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
            .UseLamar()
            .ConfigureWebHostDefaults(webBuilder =>
            {
                    webBuilder.UseStartup<Startup>();
            });
    }

Startup:

        public void ConfigureContainer(ServiceRegistry services)
        {
            services.Scan(scanner =>
            {
                scanner.TheCallingAssembly();
                scanner.WithDefaultConventions();
                scanner.SingleImplementationsOfInterface();
            });
            
            services.Policies.SetAllProperties(y => y.OfType<ISetter>());
        }

        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            // debug
            var container = (IContainer)app.ApplicationServices;
            var plan = container.Model.For<WeatherForecastController>().Default.DescribeBuildPlan();
            Console.WriteLine(plan); // this shows both inline AND setter in the build plan

            app.UseRouting();

            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });
        }

Some POCO + Interface:

    public interface ISetter
    {

    }

    public class Setter : ISetter
    {

    }

Controller:

    [ApiController]
    [Route("[controller]")]
    public class WeatherForecastController : ControllerBase
    {
        [SetterProperty] // doesn't help
        public ISetter setter { get; set; } // always null, with, or without attribute
    
        ...derp...

        public WeatherForecastController(ISetter _setter) // this works
        {

        }
    }

I’ve tried every flavor of setter / property injection, nothing seems to work. I had this working in StructureMap though.

Side note: when using a public abstract class without a constructor, the build plan throws an error. I thought this was my original problem but when I did a vanilla setter injection test as described above, it seems to just be broken.

Example:

    public abstract class BaseController : ControllerBase
    {
        // even when setup properly these are always null. 
        public IMediator _mediator { get; set; }
        public IHubContext<LogHub> _logHubContext { get; set; }
        public IConfiguration _config { get; set; }

Issue Analytics

  • State:closed
  • Created 4 years ago
  • Comments:14 (7 by maintainers)

github_iconTop GitHub Comments

2reactions
stevestokescommented, Jan 5, 2020

@jeremydmiller I figured it out. Definitely not a Lamar issue, just an MVC setup issue. You may want to add this to your asp.net Core 3.x docs

services.AddMvc().AddControllersAsServices();

For some reason adding AddControllersAsServices(); fixes it.

0reactions
stevestokescommented, Jan 4, 2020

So I did some investigation. I could use a breadcrumb, you may know where to look. I can see that Jasper is creating the correct code to inject the setter, I get this from DescribeBuildPlan() specific to WeatherForecastController:

using System.Threading.Tasks;

namespace Jasper.Generated
{
    // START: WebApplication1_Controllers_WeatherForecastController_weatherForecastController
    public class WebApplication1_Controllers_WeatherForecastController_weatherForecastController : Lamar.IoC.Resolvers.TransientResolver<WebApplication1.Controllers.WeatherForecastController>
    {


        public override WebApplication1.Controllers.WeatherForecastController Build(Lamar.IoC.Scope scope)
        {
            var setter3 = new WebApplication1.Setter();
            return new WebApplication1.Controllers.WeatherForecastController(){APublicSetterProperty = setter3};
        }

    }

    // END: WebApplication1_Controllers_WeatherForecastController_weatherForecastController
    
    
}

WhatDidIScan() includes my only assembly and WhatDoIHave() does show ISetter. All of this is looking good.

I did a direct test on the container in Startup.cs, testing as so: var controller = container.GetInstance<WeatherForecastController>(); This worked as expected, the public property APublicSetterProperty was set and not null.

I noticed when debugging the Lamar code when I call DescribeBuildPlan() or GetInstance<T>() or GetService(Type serviceType)

The code will call down into ContructorInstance.cs and into the findSetters() method:

private void findSetters(ServiceGraph services)
        {
            foreach (var property in ImplementationType.GetProperties().Where(x => x.CanWrite && x.SetMethod.IsPublic))
            {
                var instance = findInlineDependency(property.Name, property.PropertyType);
                if (instance == null && services.ShouldBeSet(property))
                {
                    instance = services.FindDefault(property.PropertyType);
                }

                if (instance != null) _setters.Add(new InjectedSetter(property, instance));
            }
            
            foreach (var setter in _setters)
            {
                setter.Instance.CreatePlan(services);
            }
        }

This is what I would expect. However, when I comment out my calls to DescribeBuildPlan, GetInstance and GetService it looks like whatever is resolving the MVC Controller WeatherForecastController it is not following the same or similar code paths within IoC\Scope.cs. It looks like the bootstrapping to the Asp Core dependency resolver is not calling into Lamar to instance the MVC Controller? It looks like most of this happens in IoC\Scope.cs, but I am not able to locate where Asp Core calls into Lamar to instance Controller classes, do you know where that entry point is?

Side note, I may be wrong on some of this as I am just learning the code.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Inject and @Autowired not working whereas ...
1 Answer 1 · 1. Use a setter as per your example and remove the @Inject. This is the simplest approach since you...
Read more >
Understanding Dependency Injection in .NET Core
This tutorial will try to clarify the various Dependency Injection concepts and will introduce you to the support provided by .NET Core. The...
Read more >
Dependency Injection in ASP.NET Core
ASP.NET Core injects objects of dependency classes through constructor or method by using built-in IoC container. Built-in IoC Container. ASP.NET Core framework ...
Read more >
3.4.1 Dependency injection
The Spring team generally advocates setter injection, because large numbers of constructor arguments can get unwieldy, especially when properties are optional.
Read more >
Setter Injection | Lamar
If setter injection is not working for you, try to look at the WhatDoIHave() output and type scanning. Lamar can inject dependencies into...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found