0

I am trying to understand Dependency Injection where usually everything is injected as either via Constructor or Property Injection.

So far I have understood that it basically revolves around interface to mock the class.

I am checking out Nop Commerce where I came across CustomerModelFactory which accepts couple of Domain class like CustomerSettings,DatetimeSettings etc..

Now when I check the DependencyRegistrar.cs class, I don't see how the dependency registration or even in the same class, I don't see the new instance of CustomerSettings created anywhere.

So my question is when we inject concrete class in constructor of class, where do we register it or how IOC container supply the instance?

CustomerModelFactory.cs

 public partial class CustomerModelFactory : ICustomerModelFactory
    {
    // all below are concrete class
      public CustomerModelFactory(AddressSettings addressSettings,
            CaptchaSettings captchaSettings,
            CatalogSettings catalogSettings,
            CommonSettings commonSettings,
            CustomerSettings customerSettings,
            DateTimeSettings dateTimeSettings,
    }

DependencyRegistrar.cs

 public class DependencyRegistrar : IDependencyRegistrar
    {
       
        public virtual void Register(ContainerBuilder builder, ITypeFinder typeFinder, NopConfig config)
        {
          builder.RegisterType<CustomerModelFactory>().As<ICustomerModelFactory>().InstancePerLifetimeScope();
    }
}

I couldn't find where below is done:

CustomerSettings settings = new CustomerSettings();
    or
CatalogSettings settings = new CatalogSettings();

How can I understand how this is working?

halfer
  • 19,824
  • 17
  • 99
  • 186
I Love Stackoverflow
  • 6,738
  • 20
  • 97
  • 216
  • `or how IOC container supply the instance?` When you hit a page on the website, MVC has "hooks" to let you do stuff (like DI). So, when you register a dependency, the IoC container takes advantage of these hooks and says "hey, I'll take care of creating that controller and all of its dependencies". It is a bit like going to the supermarket and they give me a trolley on the way in. It isn't _my_ concern how they get that trolley (they likely have some dude walking around getting them from the car park - that is the registration). But I expressed my dependency on the trolley, so I was given it. – mjwills Feb 09 '21 at 22:28
  • @mjwills Yeah but my question is I don’t know know from where the instance of this concrete classes are provided in the whole project. Seems like a magic. I couldn’t find anywhere. Could you tell me something about how this DI is figuring out instance of all the concrete classes which are injected? – I Love Stackoverflow Feb 10 '21 at 00:42
  • 1
    `builder.RegisterType().As().InstancePerLifetimeScope();` says "if anyone asks for `ICustomerModelFactory` then give them a `CustomerModelFactory`". So the IoC news up `CustomerModelFactory` as needed (and its dependencies, if needed), by hooking into the MVC pipeline. It feels like magic a bit, yes. – mjwills Feb 10 '21 at 02:17
  • 1
    If your goal is to learn and understand DI, directly diving into the code base of a complex framework might not be the best approach. The code structure can be overwhelming, and you never know if all code you look at adheres to good DI practices. Instead, may I suggest reading the freely available [chapter 1](https://livebook.manning.com/book/dependency-injection-principles-practices-patterns/chapter-1) (or its [summary](https://freecontent.manning.com/dependency-injection-writing-maintainable-loosely-coupled-code/)) of [DIPP&P](https://mng.bz/BYNl). – Steven Feb 10 '21 at 08:22
  • @mjwills Yes, I do understand that with Interface and its corresponding implementation, but when its just the concrete implementation then how to register it with DI? Thats what I am trying to understand – I Love Stackoverflow Feb 10 '21 at 16:50
  • `builder.RegisterType().As().InstancePerLifetimeScope();` – mjwills Feb 10 '21 at 21:22
  • @mjwills But is it a good practics to register domain or DTO models with IOC containers? – I Love Stackoverflow Feb 12 '21 at 18:41
  • As a general rule, DTOs shouldn't be registered. – mjwills Feb 12 '21 at 23:07
  • @mjwills So is it fine newing up DTO ? Does it violate any principles when we are working with Repositiory pattern, using IOC containers etc? – I Love Stackoverflow Feb 12 '21 at 23:15
  • Yes, it is fine. – mjwills Feb 12 '21 at 23:22

2 Answers2

2

That's why DI does not really reduce complexity, instead, it hides complexity under surface and offload lifecycle management to another thing that you don't really know too much, as each DI framework is different. Anyway, that is another topic.

Here is to answer your question, ignore which DI framework, just think in general, there are 3 ways for you to get an instance of an object

  1. Create the instance directly when you need it
CustomerSettings settings = new CustomerSettings();
  1. Create the instance by Reflection when you need it
Type t = typeof(CustomerSettings);
CustomerSettings settings = Activator.CreateInstance(t) as CustomerSettings;
  1. Cache all instances in a dictionary and look up when using the type name

something can be like this:

Dictionary<Type, object> lookup;
lookup.Add(typeof(CustomerSettings), new CustomerSettings()): 

(This way does not generate a new instance though). Now if you need the instance, you ask the dictionary to give it to you

lookup[typeof(CustomerSettings)]

This action, is called Resolved in many DI framework.


How does the DI framework find it though?

To do this, many DI framework will use reflection to find the matching type. There should always a process to register the types you want DI framework to resolve automatically. It means, you tell DI framework what type it needs to be aware, and then give it back to me when you look up using the type.

For example, you may see code like this:

container.Register<CustomerSettings>(); 

In this case, CustomerSettings is a class type, so DI knows how to create it when you need it.

However, if you are registering an interface

container.Register<ICustomerSettings, CustomerSettings>(): 

The above is one syntax to register interface and its concrete type. Basically, you tell DI, this is the type, and that is the implementation. So when you do this:

var setting = container.Resolve<ICustomerSettings>();

You will get an instance of CustomerSettings.

It will work if you have multiple implementations of the same interface, but you need some special handling. Different DI handles it differently.

Hopefully so far it makes a little sense.

Each DI framework has an IOC container, which acts like a dictionary. You register the type into there, and ask it to give it back.

There are more details, but I will not cover in here.

sowen
  • 1,090
  • 9
  • 28
  • Upvoted for your kind efforts towards helping me and thanks for such a comprehensive answer. but still I am confused that where the object of concrete classes are created in this project. Scratching my head from 2 hours but couldnt figure where it is done – I Love Stackoverflow Feb 09 '21 at 22:05
  • Link to source code: https://www.nopcommerce.com/en/download-nopcommerce – I Love Stackoverflow Feb 09 '21 at 22:06
  • the framework will create it for you when you need it. In the case of `when` depends on your use case; in the case of `how` depends on what framework. Basically, internally, it can use reflection to create the concrete instance, or it will use a lookup and return the object it has already created – sowen Feb 09 '21 at 22:12
  • basically, you can think about if you have to write a `container` how are you going to return the concrete class when you ask it? it's fairly easy to create a simple one for yourself (very simple one), for example, you can have a singleton class with a dictionary internally, and every time you ask for a type, internally, you can just create one and return (and then put it in the dictionary); next time you need the same type, you just return from the dictionary. very similar idea – sowen Feb 09 '21 at 22:14
1

Concrete types are not automatically resolved by MS.DI; they need to be registered explicitly. NopCommerce, therefore, registers them inside its DependencyRegistrar class (on line 241):

//register all settings
var settings = typeFinder.FindClassesOfType(typeof(ISettings), false).ToList();
foreach (var setting in settings)
{
    services.AddScoped(setting, serviceProvider =>
    {
        var storeId = DataSettingsManager.IsDatabaseInstalled()
            ? serviceProvider.GetRequiredService<IStoreContext>()
                 .GetCurrentStoreAsync().Result?.Id ?? 0
            : 0;

        return serviceProvider.GetRequiredService<ISettingService>()
            .LoadSettingAsync(setting, storeId).Result;
    });
}
Steven
  • 166,672
  • 24
  • 332
  • 435
  • Upvoted for your kind efforts towards helping me and wow this was really hard to figure out from my end. Thank you so much. Really appreciate it. Actually when I see the Nop commerce Isetting Interface, there is nothing inside that interface. I have never seen blank interface in any of the project. Do you know why it is like that? I mean why anyone would ever create a blank interface? – I Love Stackoverflow Feb 10 '21 at 16:46
  • 2
    It's called a "Marker Interface". The Framework Design Guidelines warn against it. Still, I find them useful from time to time, because their use can help communicate intend, while letting the compiler help enforcing its use. Another use is to allow assembly scanning, which is what Nop seems to use it for. – Steven Feb 10 '21 at 17:30
  • Wow this looks interesting:). Also there is an CaptchaHttpClient class but I dont see where it is registered in DependencyRegistrar and where its instance is created – I Love Stackoverflow Feb 10 '21 at 18:24