5

Earlier I asked this question. The answer to which resulted into another question like thou seeth before thee.

The initial problem

My problem is, that I have a custom MembershipProvider using an AccountRepository using an ObjectContext. Because the MembershipProvider is a Singleton in MVC (as I understand), the AccountRepository and its ObjectContext should be injected once and stay there for the remainder of the MembershipProvider's life time.

However, in my controllers I also use repositories with object contexts. In these controllers I need the object context to be shared between the repositories with a request. I have the following binding:

Bind<IMyContext>().To<MyObjectContext>().InRequestScope();
// put bindings here
Bind<IAccountRepository>().To<EFAccountRepository>

and in the Application_Start()

kernel.Inject(Membership.Provider);

The problem is that Ninject apparently calls dispose on the object context when it thinks the request is done (I think after 30 seconds).

My (not-working) solution

I noticed that when you set your bindings you can specify "when injecting into". The problem is, that I need "when injecting into when injecting into". I.e. when injecting the object context into the account controller when injecting the account controller into the membership provider. And I don't seem to have that...

Work-arounds I thought of (but don't really like).

  1. Don't hang MyMembershipProvider in MVC. Just pas an instance of it (behind and interface) to the controllers that need it like I do with repositories. Then Ninject will instantiate the provider per request. I don't like it because I'm sure MVC has a reason for instantiating the membership provider as a singleton.
  2. Find an event that occurs every request and call kernel.Inject again in each event. Re-initializing the provider every request is nearly equivalent to re-instantiating, except dirtier.
  3. Make a separate account repository for the membership provider to which I can bind in a different way. It doesn't seem right to change my object model because of Ninject.

Conclusion

IMHO the first work-around is the best. However, I much rather find a way to set Ninject to bind in the way I want.

What should I do?

Community
  • 1
  • 1
Matthijs Wessels
  • 6,530
  • 8
  • 60
  • 103
  • 1
    I'm +1'ing this because of the use of the singular personal pronoun. It's ok, we're bringing it back :) – Khanzor May 02 '12 at 23:59

1 Answers1

3

The .InRequestScope() bit is causing some of your objects to be Disposed (see Cache and Collect).

You need to make this (scoping by request) not happen in your provisioning of your provider's dependencies.

One way to achieve that is by having two bindings - one for the request processing branch and one for the global context, i.e., adding a When... which uses the context.ParentContext... chain to control whether it's InRequestScope() or not

Sadly I don't have time to give a full answer now and the relevant docs are out of sync with the code at present.

Ruben Bartelink
  • 59,778
  • 26
  • 187
  • 249
  • Ah, so it is possible to specify more precisely when to inject what. I'll experiment a bit and report back if I find anything. – Matthijs Wessels Mar 14 '11 at 13:36
  • @Matthijs: Yes, there's a mechanism to travel up the request tree as you want, and When has a generalised mechanism that allows you to exploit that. The metadata mechanism allows you to have twin bindings and then control which one applies. I really need to go write this in the docs where it belongs though! BTW I've recently revised the docs and am interested in any feedback, + or - re which sections a) need to go b) need to be expanded or c) are missing. This stuff is clearly a (c) but the other two categories are hard for someone that's spent ages looking at the source to divine! – Ruben Bartelink Mar 14 '11 at 13:39
  • @Ruben: I'll remember that. If I have any comments I'll report them (through the website I suppose). I am quite new to Ninject, so don't expect too much from me :). – Matthijs Wessels Mar 14 '11 at 14:04
  • I am stuck on determining how to check the scope. There is a function GetScope() that returns an object. I don't know what to compare it with. – Matthijs Wessels Mar 14 '11 at 14:44
  • @Matthijs: You dont get to check the scope -- you need to register two bindings and have a `When` on each one that inspects the context in order to say "use this binding [with this scope] when its going into the Membership provider" and "use this binding [when it's not]". – Ruben Bartelink Mar 14 '11 at 17:46
  • Or you could put a marker attribute on the target called "NotInRequestScope" and then add a 'duplicate' binding that has a WhenTargetHas() – Ruben Bartelink Mar 14 '11 at 17:47
  • I clearly need to update that article but am in the middle of something else right now, sorry... – Ruben Bartelink Mar 14 '11 at 17:48
  • Thanks for your help, your last suggestions will probably help me further. For us this issue has been put on hold (solved now with a hack. I re-inject the AccountRepository in every request) until the next iteration (starting Monday, don't worry ;) ). – Matthijs Wessels Mar 16 '11 at 11:17
  • @Matthijs Wessels: Article updated. Not sure if its directly useful enough yet tho'.... – Ruben Bartelink Apr 02 '11 at 20:41
  • @Ruben: Thanks, I will look at it when I get time to fix the workaround. – Matthijs Wessels Apr 04 '11 at 11:43
  • @MatthijsWessels Did you ever figure this out? I'm in the same situation as you and I'm not sure of the best approach. To be honest your 'hack' approach sounds the most appealing to me. I'm not sure if the duplicate bindings is appropriate, because then won't we have two separate instances of the ObjectContext? What I really want is to give the same ObjectContext to my MembershipProvider that I'm giving to my repositories. – Pandincus Mar 02 '12 at 15:18