20

I have a project where the Ninject is used as IoC container. My concern is that a lot of classes have such kind of constructors:

[Inject]
public HomeController(
    UserManager userManager, RoleManager roleManager, BlahblahManager blahblahManager) {
   _userManager = userManager;
   _roleManager = roleManager;
   _blahblahManager = blahblahManager;
}

What if I don't want to have all instances of these classes at once?

The way, when all this classes are wrapped by Lazy<T> and passed to constructor is not exactly what I need. The T instances are not created yet, but Lazy<T> instances are already stored in memory.

My colleague is suggesting me to use Factory pattern to have control over all instantiations, but I'm not sure that IoC have such great design bug.

Is there a workaround for this situation or IoC really have such big defect in it's design? Maybe I should use another IoC container?

Any suggestions?

Steven
  • 166,672
  • 24
  • 332
  • 435
xwrs
  • 1,287
  • 3
  • 16
  • 29
  • What actually is your problem? Why don't you want these instances? – Daniel Hilgarth Sep 05 '12 at 15:11
  • 2
    I may want UserManager during the work of controller, but RoleManager is not needed and vice-versa. If you talking about the Lazy instances, then this is not a big deal to have them in memory, but is this the only way? – xwrs Sep 05 '12 at 15:13
  • 2
    Why is it a big deal for `UserManager` and `RoleManager`? Your constructors shouldn't do heavy work anyway. – Daniel Hilgarth Sep 05 '12 at 15:16
  • 2
    They are stored in memory and if they are really complex it can cause performance issues. Also, the example contains only three injected classes, but if there is more... – xwrs Sep 05 '12 at 15:18
  • 1
    No, it usually won't cause performance issues. Do you have a performance issue in your case? – Daniel Hilgarth Sep 05 '12 at 15:19
  • Not at this time, but the team members had a great discussion what to use: IoC or Factory. – xwrs Sep 05 '12 at 15:21
  • 1
    Use IoC in this case. Use a factory - which will be injected using IoC! - if you really need control over the lifetime of the object. – Daniel Hilgarth Sep 05 '12 at 15:24
  • Some containers let you inject a `Func` for lazy dependency resolution, I'm not sure whether Ninject does though. – Lee Sep 05 '12 at 15:35
  • @Lee: Which in fact is nothing more than a factory. – Daniel Hilgarth Sep 05 '12 at 15:36
  • 2
    @DanielHilgarth - Yes, although it means you don't need to create an explicit factory type. – Lee Sep 05 '12 at 15:38
  • 3
    Every container allows you to inject a `Func` or a `Lazy`. Just register that `Func` or `Lazy` manually in the container. – Steven Sep 05 '12 at 16:02

3 Answers3

52

Seems to me that you are doing premature optimization: don't do it.

The constructors of your services should do nothing more than storing the dependencies that it takes in private fields. In that case the creation of such an object is really light weight. Don't forget that object creation in .NET is really fast. In most cases, from a performance perspective, it just doesn't matter whether those dependencies get injected or not. Especially when comparing to the amount of objects the rest of your application (and the frameworks you use) are spitting out. The real costs is when you start using web services, databases or the file system (or I/O in general), because they cause a much bigger delay.

If the creation is really expensive, you should normally hide the creation behind a Virtual Proxy instead of injecting a Lazy<T> in every consumer, since this allows common application code to stay oblivious to the fact that there is a mechanism to delay the creation (both your application code and test code are becoming more complex when you do this).

Chapter 8 of Dependency Injection: Principle, Practices, Patterns contains a more detailed discussion about lazy and Virtual Proxies.

However, a Lazy<T> just consumes 20 bytes of memory (and another 24 bytes for its wrapped Func<T>, assuming a 32bit process), and the creation of a Lazy<T> instance is practically free. So there is no need to worry about this, except when you’re in an environment with really tight memory constraints.

And if memory consumption is a problem, try registering services with a lifetime that is bigger than transient. You could do a per request, per web request, or singleton. I would even say that when you're in an environment where creating new objects is a problem, you should probably only use singleton services (but it's unlikely that you're working on such an environment, since you're building a web app).

Do note that Ninject is one of the slower DI libraries for .NET. If that's troubling you, switch to a faster container. Some containers have performance that is near newing up object graphs by hand. but by all means, do profile this, many developers switch DI libraries for the wrong reasons.

Do note that the use of Lazy<T> as dependency is a leaky abstraction (a violation of the Dependency Inversion Principle). Please read this answer for more information.

Steven
  • 166,672
  • 24
  • 332
  • 435
  • I don’t think it’s premature optimization, with this argument it seems there is never any need for Lazy, creating one object is not heavy but you don’t know how many objects it is creating in its constructor. And opening some remote resource which may not be used. – Akash Kava Mar 01 '18 at 05:51
2

Steven is correct in saying that this looks like premature optimization. The construction of these object is very fast and is usually never the bottleneck.

However using Lazy to express a dependency you don't need right away is a common pattern in Dependency Injection frameworks. Actofac is one such container that has built in support for various wrapping types. I'm sure there is also an extension for Ninject as well, maybe take a look at this one, Ninject Lazy.

Daniel Little
  • 16,975
  • 12
  • 69
  • 93
  • 3
    The fact that DI containers have support for Lazy, doesn't make it right to let your application code depend on a Lazy dependency. Many DI containers support features that don't promote best practices. From a Dependency Injection perspective, Lazy is a leaky abstraction. Please read [this](https://stackoverflow.com/a/21724609/264697) for an explanation on why Lazy leaks. – Steven Nov 18 '14 at 12:52
1

You can also inject into an action method with the syntax below. (I'm not sure exactly what version this was introduced).

Constructor is best practice, but I had to do this once deliberately when I had a service that was doing some expensive initialization - accidentally in fact - but it wasn't discovered for a while and it was just easiest to move it to the one method that did need it.

This can make for cleaner code if you only need to access a service from one action method - but bear in mind if you inject it to the method you'll have to pass it around everywhere because it will no longer be on this. Definitely don't go assigning to this.service in an action method - that's horrible.

public IActionResult About([FromServices] IDateTime dateTime)
{
    ViewData["Message"] = "Currently on the server the time is " + dateTime.Now;

    return View();
}

https://learn.microsoft.com/en-us/aspnet/core/mvc/controllers/dependency-injection?view=aspnetcore-2.2#action-injection-with-fromservices

Simon_Weaver
  • 140,023
  • 84
  • 646
  • 689