C
C#2y ago
big OOF

❔ Understanding inheritance

Hello, im trying to understand a webshop example from microsoft. Im trying see what GetCardTypesAsync() does. (Attatched is the code from OrdersController)
When peeking the definition i only get to the interface "IOrderQueries" used by the OrdersController. I find the original function in another cs file "OrderQueries" (by searching for the function name) which inherits the interface but i really cant understand the connection between Orderqueries, the interface IOrderQueries and OrdersController. How does GetCardTypesAsync() in OrdersController know to use the function declared in OrderQueries just by using a interface? Anyone see what i dont? Let me know if you need additional info 🙂 Thanks in advance!
17 Replies
big OOF
big OOF2y ago
My guess is its thanks to the dependency injection in OrdersController.
private readonly IMediator _mediator;
private readonly IOrderQueries _orderQueries;
private readonly IIdentityService _identityService;
private readonly ILogger<OrdersController> _logger;

public OrdersController(
IMediator mediator,
IOrderQueries orderQueries,
IIdentityService identityService,
ILogger<OrdersController> logger)
{
_mediator = mediator ?? throw new ArgumentNullException(nameof(mediator));
_orderQueries = orderQueries ?? throw new ArgumentNullException(nameof(orderQueries));
_identityService = identityService ?? throw new ArgumentNullException(nameof(identityService));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
private readonly IMediator _mediator;
private readonly IOrderQueries _orderQueries;
private readonly IIdentityService _identityService;
private readonly ILogger<OrdersController> _logger;

public OrdersController(
IMediator mediator,
IOrderQueries orderQueries,
IIdentityService identityService,
ILogger<OrdersController> logger)
{
_mediator = mediator ?? throw new ArgumentNullException(nameof(mediator));
_orderQueries = orderQueries ?? throw new ArgumentNullException(nameof(orderQueries));
_identityService = identityService ?? throw new ArgumentNullException(nameof(identityService));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
But if you inject the dependency to the IQueryInterface, how does that make the function from OrderQueries avaliable?
undisputed world champions
that's what DI does it provides concrete instances of interfaces & classes in the example ms apparently chose to implement IOrderQueries in a class called OrderQueries and registered it in the DI-container as the implementation so whenever a class (for example one of the controllers) asks for a dependency of type IOrderQueries it will be given a instance of OrderQueries the controllers don't need to worry what concrete class it is. all it cares that it implements IOrderQueries. that's part of the problem DI/IoC try to solve 😉
big OOF
big OOF2y ago
Great anser, thanks! I think i kinda get it! So when you instantiate the interface it will include all the classes that implemts the interface? Made a small sketch to visualise my thought, am i getting it correctly? 🙂
undisputed world champions
there can be multiple classes implementing the interface but in you DI-container you will register one of them as the default implementation for the interface (basically saying "whenever someone asks for interface IOrderQueries give them an object of class OrderQueries") so when Class2 or OrdersController need a IOrderQueries they will be given an OrderQueries you cant instantiate an interface. you can only instantiate classes that implement an interface 😉 interfaces are a little bit like a specification. they describes how classes should look like, to allow certain scenarios (in this case "querying orders")
big OOF
big OOF2y ago
Ahh okey now i think i get it - you declare in the DI-container the terms of which class that should be used. There i can see it being very useful, only the class that is needed for the specfic class/situation is instantiated. I think my confusion comes from that i cant see where the class OrderQueries "selected as the class to instantiate", but that is done in the DI then? This might be a stupid question but can i find the DI-container and how does it tell which class that are needed? Im looking for the class or file that tells the program which class that should be implemented. There are alot of files in this project, are there any general naming conventions or such that can help me find the DI-container? 🙂
undisputed world champions
there probably is a class Startup that contains a function ConfigureServices. in there the registrations for DI are done (should use functions like AddScoped<...>(...) or AddTransient<...>(...) alternatively it might be done directly in the Program class
big OOF
big OOF2y ago
Thank you!
public virtual IServiceProvider ConfigureServices(IServiceCollection services)
{
services
.AddGrpc(options =>
{
options.EnableDetailedErrors = true;
})
.Services
.AddApplicationInsights(Configuration)
.AddCustomMvc()
.AddHealthChecks(Configuration)
.AddCustomDbContext(Configuration)
.AddCustomSwagger(Configuration)
.AddCustomIntegrations(Configuration)
.AddCustomConfiguration(Configuration)
.AddEventBus(Configuration)
.AddCustomAuthentication(Configuration);
//configure autofac

var container = new ContainerBuilder();
container.Populate(services);

container.RegisterModule(new MediatorModule());
container.RegisterModule(new ApplicationModule(Configuration["ConnectionString"]));

return new AutofacServiceProvider(container.Build());
}
public virtual IServiceProvider ConfigureServices(IServiceCollection services)
{
services
.AddGrpc(options =>
{
options.EnableDetailedErrors = true;
})
.Services
.AddApplicationInsights(Configuration)
.AddCustomMvc()
.AddHealthChecks(Configuration)
.AddCustomDbContext(Configuration)
.AddCustomSwagger(Configuration)
.AddCustomIntegrations(Configuration)
.AddCustomConfiguration(Configuration)
.AddEventBus(Configuration)
.AddCustomAuthentication(Configuration);
//configure autofac

var container = new ContainerBuilder();
container.Populate(services);

container.RegisterModule(new MediatorModule());
container.RegisterModule(new ApplicationModule(Configuration["ConnectionString"]));

return new AutofacServiceProvider(container.Build());
}
This is from the startup file, my guess is that the logic which class that is "selected"(for example as default) comes from one of these lines, am i correct? 🙂 .AddApplicationInsights(Configuration) .AddCustomMvc() .AddHealthChecks(Configuration) .AddCustomDbContext(Configuration) .AddCustomSwagger(Configuration) .AddCustomIntegrations(Configuration) .AddCustomConfiguration(Configuration) .AddEventBus(Configuration) .AddCustomAuthentication(Configuration); As a example, if you were to change which class is given as default, you would have to look into one of the mentioned above? 🙂
undisputed world champions
yes those functions will register a bunch of services at once if you want to override the registration you can simply call the appropriate AddScoped<...>(...)/AddTransient<...>(...)/AddSingleton<...>(...) after those functions like services.AddScoped<IOrderQueries, MyNewOrderQueries>(), will use MyNewOrderQueries instead of the "normal" OrderQueries
big OOF
big OOF2y ago
Ah okey, i think i just realised something! Whenever i add something to IServiceCollection services i can say that i added it to the DI-container, right? For example: .Service.AddMVC just adds the classes that are needed for MVC to the DI-container? -> and are therefore avaliable in whatever class we instantiate it in? 😄
undisputed world champions
yes IServiceCollection is microsofts DI-container that comes with asp.net (can be used in other apps too) there are other DI-containers too, but since this one is the default, it has become the most popular ... or at least the most known one 😉 "DI-container" is a more general term, so to say
big OOF
big OOF2y ago
Damn, my mind is blown! Ive had such a hard time grasping the whole DI-container term. Cant thank you enough! Something that still is a bit vague to me is where the conditions or default class is set for the interface. I attached the Startup file and added a comment under CunfigureServices. Im thinking it would look something like this:
//.AddServiceThatKnowsWhichClassAreNeeded(Configuration);

public static IServiceCollection AddServiceThatKnowsWhichClassAreNeeded(this IServiceCollection services, IConfiguration configuration)
{
//Does something that tells the DI which class to return when interface IOrderQueries is called
return services;
}
//.AddServiceThatKnowsWhichClassAreNeeded(Configuration);

public static IServiceCollection AddServiceThatKnowsWhichClassAreNeeded(this IServiceCollection services, IConfiguration configuration)
{
//Does something that tells the DI which class to return when interface IOrderQueries is called
return services;
}
Im having a hard time to really understand everything in the startup file, maybe there is a conditions that ish-does what i mentioned above but that i just cant relly see it with my own eyes. Can you tell? Or maybe i misunderstood something? 🙂
big OOF
big OOF2y ago
@icebear
undisputed world champions
this seems to be a more complicated example it is using autofac for DI (as is said there are multiple DI-containers) first it registers a bunch of services in IServiceCollection then creates a autofac container and populates it with the registrations from IServiceCollection (var container = new ContainerBuilder(); container.Populate(services);) then it registers two modules to the autofac-container container.RegisterModule(new MediatorModule()); container.RegisterModule(new ApplicationModule(Configuration["ConnectionString"])); in the ApplicationModule there is the registration for IOrderQueries:
builder.Register(c => new OrderQueries(QueriesConnectionString))
.As<IOrderQueries>()
.InstancePerLifetimeScope();
builder.Register(c => new OrderQueries(QueriesConnectionString))
.As<IOrderQueries>()
.InstancePerLifetimeScope();
big OOF
big OOF2y ago
Okey, so in a way this solution is using two DI-containers? IserviceCollection and autofac or is autofac an extension of IserviceCollection? Another question since im not that experienced with lambda expressions. If you were to transform the code below into words, can you say: - When something is registred, is it of type IOrderQueries -> return a OrderQueries object? 🙂
builder.Register(c => new OrderQueries(QueriesConnectionString))
.As<IOrderQueries>()
.InstancePerLifetimeScope();
builder.Register(c => new OrderQueries(QueriesConnectionString))
.As<IOrderQueries>()
.InstancePerLifetimeScope();
undisputed world champions
kinda yes. most libraries provide those Add*() functions that work with IServiceCollection that's why it first uses IServiceCollection and then copies over the registration into the autofac-container (that is going to be used from then on) and yes the lambda basically will be called anytime a object for IOrderQueries needs to be created
big OOF
big OOF2y ago
Okey perfect, then i think i understand. Cant thank you enough, great educator, great answers and maybe even greater patience with a noob like me 😉 you made my day!
Accord
Accord2y ago
Was this issue resolved? If so, run /close - otherwise I will mark this as stale and this post will be archived until there is new activity.