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

xvdiff
  • 2,179
  • 2
  • 24
  • 47
  • 1
    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... – Alexei Levenkov Jan 06 '15 at 16:23
  • 1
    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 variable – MethodMan Jan 06 '15 at 16:23
  • @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. – xvdiff Jan 06 '15 at 16:28
  • 1
    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). – danludwig Jan 06 '15 at 16:38
  • @danludwig That was just from copy paste (not in real code). Fixed in now and added stack. – xvdiff Jan 06 '15 at 16:50
  • 1
    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? – danludwig Jan 06 '15 at 16:58
  • @danludwig 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! – xvdiff Jan 06 '15 at 17:04
  • can you show code of `TryDo` extension? – Viktor Lova Jan 07 '15 at 17:02
  • @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. – xvdiff Jan 07 '15 at 17:05
  • 1
    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. – Viktor Lova Jan 07 '15 at 17:23
  • Does your MyController class have a constructor that's being called which might not be setting _repo? – Ryan Jan 09 '15 at 20:28
  • Does anything change if you don't dispose your UoW object in EfUnitOfWorkInterceptor ? – jmsb Jan 21 '15 at 17:34
  • @xvdiff, What is the scope of your IRepo regarding to NInject ? Could it be a singleton or something in this fashion ? – John-Philip Jan 26 '15 at 13:25
  • 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. – Mick Jan 29 '15 at 02:48
  • Are you calling a `virtual` method that's being overridden in a more derived type in any of your constructors? – aevitas May 04 '15 at 11:06

2 Answers2

1

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.

fred
  • 693
  • 1
  • 7
  • 19
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.

hangy
  • 10,765
  • 6
  • 43
  • 63