Private member is suddenly null on API method call
Asked Answered
G

3

13

Weird stuff going on: In my web api, I inject a repository into the controller upon resolving using Ninject. The repository gets stored in a private readonly member variable. Works perfectly fine! When a api method gets called, I access the variable - only to see that it's suddenly null!

Pseudo example:

public class MyController : ApiController {

  private readonly IRepo _repo;

  public MyController(IRepo repo) {
     Guard.AgainstNullArgument("repo", repo); // guarding to 
                                                          // make sure it's not null
                                                          // (would throw ex)
     _repo = repo; <--- successfully injected
  }

  // calling this method
  public HttpResponseMessage TestMethod() {
     _repo.. <--- suddenly null
  }

}

I've traced down the problem to a tiny little detail: One of the methods in the controller (not the one that get's accessed) is annotated with a custom attribute that directs ninject to intercept the method with a unit of work. If I take the attribute away, everything magically works again.

UnitOfWorkAttribute.cs

[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class | AttributeTargets.Interface)]
public class UnitOfWorkAttribute : Attribute
{
}

AttributeInterceptionStrategy.cs (for ninject)

http://pastebin.com/Qg6tQWye

StartupConfig.cs (composition root, IoC configuration etc.)

http://pastebin.com/fcuSdujj

EfUnitOfWorkInterceptor.cs

public class EfUnitOfWorkInterceptor : SimpleInterceptor
{

    private readonly IUnitOfWork _unitOfWork;

    public EfUnitOfWorkInterceptor(IUnitOfWork unitOfWork)
    {
        Guard.AgainstNullArgument("unitOfWork", unitOfWork);
        _unitOfWork = unitOfWork;
    }

    protected override void AfterInvoke(IInvocation invocation)
    {
        if(!_unitOfWork.Commited)
            _unitOfWork.Commit();

        _unitOfWork.Dispose();
    }
}

EDIT

I've literally put breakpoints everywhere to figure out what's going on. Made a destructor on the controller to make sure the whole class doesn't get garbaged and also changed the readonly member to a property with getter/setter where I break-pointed on the setter to check if it's assigned twice. Nothing suspicuous happens at all.

EDIT 2

Stack

http://pastebin.com/pQULHLT0

Ninject.Extensions.Interception.dll!Ninject.Extensions.Interception.Injection.Dynamic.DynamicMethodInjector.Invoke(object target = {EIT.Management.Configuration.Web.Api.Controllers.SetupGroupController}, object[] arguments = {object[2]})   Unbekannt
    Ninject.Extensions.Interception.dll!Ninject.Extensions.Interception.Invocation.Invocation.CallTargetMethod()    Unbekannt

EDIT 3*

Real world code: http://pastebin.com/SqpR9KNR

Gainsay answered 6/1, 2015 at 16:14 Comment(15)
Have you tried to add breakpoint to constructor(s) of your controller and check call stack? Likely someone code creates another controller without your injected dependency...Bebe
put break points in your code I am wondering if you are overriding the value meaning is there something triggering a postback..? or Invoking the Constructor Event.. have you thought about storing that in a Session variableJenna
@AlexeiLevenkov I did indeed. The controller is getting called only once, also I've check for null-values in my real-world controller and throw exception if it doesn't get injected correctly. See my edit.Gainsay
Maybe this is just an error in the code you posted, but it doesn't look like your constructor should compile. It gets a repo argument, but then you are guarding a different variable, unitOfWork against null. Also, you should let the action execute with the null reference, and then capture the full stack trace to add to your question. I am curious what the caller of the action method with the null controller dependency is, MVC or Ninject (or some other tool).Bette
@Bette That was just from copy paste (not in real code). Fixed in now and added stack.Gainsay
Interesting. The other action method that is decorated with the UoW attribute, could you post the code for that as well? Is it an async action method? When you invoke it, is the same readonly field also null?Bette
@Bette I've included a pastebin of the two real controller methods, the one that doesn't work (where the repo is null) and the other one that isn't involved in the call, but annotated with the attribute in question. And yes, the field is also null there. Thank you very much for your time!Gainsay
can you show code of TryDo extension?Luggage
@nsinreal Currently on the phone, so no, sorry. TryDo is just a monadic extension method for reference types which takes an action callback and wraps it in a try-catch-statement before execution.Gainsay
Also, try to create in controller private parameterless constructor and add breakpoint on it. Also create a static list (and add instances of your controller to this list on creation). Why I ask about it: it's possible to do some hacks on type system like ignoring your constructors.Luggage
Does your MyController class have a constructor that's being called which might not be setting _repo?Is
Does anything change if you don't dispose your UoW object in EfUnitOfWorkInterceptor ?Lew
@xvdiff, What is the scope of your IRepo regarding to NInject ? Could it be a singleton or something in this fashion ?Microscopic
Every request will create a new instance of the controller, it's pretty obvious that the constructor which injects _repo is not being called on the request to call TestMethod.Harleyharli
Are you calling a virtual method that's being overridden in a more derived type in any of your constructors?Kuroshio
S
2

It's strange so ! I guess maybe you have another constructor which has not set your _repo, then a new instance of the controller instantiated by that.

Samira answered 8/4, 2015 at 11:34 Comment(0)
K
0

If you use LinFu (which you should, because DynamicProxy still needs a parameterless constructor), the solution is pretty easy: Just make the method that's annotated with the UnitOfWorkAttribute virtual.

I found this through some testing: After some tries, I noticed that everything kind of worked if you just remove plan.Add(new ProxyDirective());. Of course, the interceptor was not applied, but this pointed to the fact that the proxy class was to blame.

Kazan answered 31/1, 2015 at 21:46 Comment(0)
G
0

Freds answer led me to my issue, basically a DI problem. I had a BackgroundService which also implemented the IHealthCheck interface, configuration:

    services.AddHostedService<MyBackgroundServiceType>();
    services.AddHealthChecks().AddCheck<MyBackgroundServiceType>();

which basically added two instances of MyBackgroundServiceType, where the second one was never called by host.RunAsync() and so also never got initialized - but it did get called for the HealthCheck, leading to the same issue the original poster had.

I changed the configuration to this:

    services.AddSingleton<MyBackgroundServiceType>();
    services.AddHostedService(x => x.GetRequiredService<MyBackgroundServiceType>());
    services.AddHealthChecks().AddCheck<MyBackgroundServiceType>(typeof(MyBackgroundServiceType).Name);

and everything worked fine.

Gemmation answered 5/3 at 9:44 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.