C
C#16mo ago
GIGA BRAIN

❔ Question about setting up dependency injection (console app with no async stuff)

So I've been trying to set up a dependency injection in my program for the first time to no avail. From what I've learned, a dependency injection allows you to use methods from another class without having to explicitly instantiate a new instance of the class which allows for loosely coupled code and less dependencies. I was told I should be injecting my DBContext instead of calling a new context every time I do a database action. I think the way I have been approaching this fix is wrong in that I'm creating an interface for my Helpers class when in reality I should be making an interface for my PhoneBookContext class. Am I on the right track? Any feedback is appreciated. Pasted code: https://paste.mod.gg/hhwsmajvkxnq/2
BlazeBin - hhwsmajvkxnq
A tool for sharing your source code with the world!
41 Replies
phaseshift
phaseshift16mo ago
I was told I should be injecting my DBContext instead of calling a new context every time I do a database action
that is true for ASP.net because it's the framework that resolves stuff from the container. If you're in a console app, you're going to have to resolve at least the root item manually yourself.
I should be making an interface for my PhoneBookContext class
No. doing helpers = new() and then new-ing up a PhoneBookContext in helpers is the opposite of dependency injection.
a dependency injection allows you to use methods from another class without having to explicitly instantiate a new instance of the class which allows for loosely coupled code and less dependencies.
That doesn't sound correct. loose coupling comes from class design and proper use of interfaces or other de-coupling mechanisms.
GIGA BRAIN
GIGA BRAINOP16mo ago
i think i see, it creates more dependencies?
phaseshift
phaseshift16mo ago
It's the same dependency. It's not injected. It's tightly coupled But its the only db context you;re going to have. It's ok to be relatively tightly coupled there But it's better to inject as it is reduced coupling
GIGA BRAIN
GIGA BRAINOP16mo ago
so instead of getting loosely coupled code from dependency injections, its reducing coupling instead
phaseshift
phaseshift16mo ago
? I dont know what youre referring to
GIGA BRAIN
GIGA BRAINOP16mo ago
this and how my definition of DI is wrong
phaseshift
phaseshift16mo ago
di is using constructors
GIGA BRAIN
GIGA BRAINOP16mo ago
constructor injection thats the kind of injection i'm trying to do here, right?
phaseshift
phaseshift16mo ago
It's what would be better. I don't see any attempt at it in your code
GIGA BRAIN
GIGA BRAINOP16mo ago
yeah im confused on how to set it up in the first place looking at the microsoft documentation, i'm assuming i need an interface
phaseshift
phaseshift16mo ago
if doing it manually, the only place 'new' should be is in Main No, interfaces not needed
GIGA BRAIN
GIGA BRAINOP16mo ago
oh could you explain what you mean by Main? people have said that before and i dont know if they mean like program.cs or something i remember there used to be a public static void main thing for csharp
phaseshift
phaseshift16mo ago
yes
GIGA BRAIN
GIGA BRAINOP16mo ago
is that what youre talking about? but doesn't every class have that
phaseshift
phaseshift16mo ago
the entry point of the program
GIGA BRAIN
GIGA BRAINOP16mo ago
oh nvm on my last statement
phaseshift
phaseshift16mo ago
No.There is exactly one main in an app (zero in pure libs)
GIGA BRAIN
GIGA BRAINOP16mo ago
And to make sure, this is in my program.cs right? or unless i define it in a different class
phaseshift
phaseshift16mo ago
what is in program.cs?
GIGA BRAIN
GIGA BRAINOP16mo ago
and don't use program.cs for my main
using PhoneBookApp;
using PhoneBookApp.Data;
using PhoneBookApp.Models;
using Microsoft.Extensions.DependencyInjection;

Menu menu = new Menu();
menu.ShowMenu();
using PhoneBookApp;
using PhoneBookApp.Data;
using PhoneBookApp.Models;
using Microsoft.Extensions.DependencyInjection;

Menu menu = new Menu();
menu.ShowMenu();
literally just this
phaseshift
phaseshift16mo ago
oh for top level statements.
GIGA BRAIN
GIGA BRAINOP16mo ago
i have dependencies everywhere uhh yeah?
phaseshift
phaseshift16mo ago
Menu menu = new Menu();
menu.ShowMenu();
Menu menu = new Menu();
menu.ShowMenu();
Yes, that is your main
GIGA BRAIN
GIGA BRAINOP16mo ago
got it, so all my "new" stuff should be in main and then i inject from there? is this what you mean by the root?
phaseshift
phaseshift16mo ago
yes. So here your root is Menu if you were using a container, you'd have something small like var menu = container.GetService<Menu>() and it would make everything else that needs to be injected into Menu's ctor but manually you might need to do
var d1 = new ...
var d2 = new ...
...
var d_n = new ...
var menu = new Menu(d1, d2, ..., d_n);
var d1 = new ...
var d2 = new ...
...
var d_n = new ...
var menu = new Menu(d1, d2, ..., d_n);
GIGA BRAIN
GIGA BRAINOP16mo ago
oh i see well i dont mind listing them all out in my main it would only be like 3 or 4 but youre saying I dont need an interface, so after would I follow the documentation as normal? i'm following this and it says after i need to register my service
GIGA BRAIN
GIGA BRAINOP16mo ago
Dependency injection - .NET
Learn how to use dependency injection within your .NET apps. Discover how to registration services, define service lifetimes, and express dependencies in C#.
GIGA BRAIN
GIGA BRAINOP16mo ago
but then im not using an interface with a concrete type so i'm confused on that should i just learn how to do DI when I learn about asp.net core?
phaseshift
phaseshift16mo ago
without an interface it's just services.AddSingleton<Menu>() as opposed to services.AddSingleton<IMenu, Menu>()
GIGA BRAIN
GIGA BRAINOP16mo ago
so something like this?
using PhoneBookApp;
using PhoneBookApp.Data;
using PhoneBookApp.Models;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

Menu menu = new();
Helpers helpers = new();
UserInput input = new();
Validate validate = new();
using PhoneBookContext _context = new();

HostApplicationBuilder builder = Host.CreateApplicationBuilder();

builder.Services.AddSingleton<Program>();

using IHost host = builder.Build();
host.Run();

menu.ShowMenu();
using PhoneBookApp;
using PhoneBookApp.Data;
using PhoneBookApp.Models;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

Menu menu = new();
Helpers helpers = new();
UserInput input = new();
Validate validate = new();
using PhoneBookContext _context = new();

HostApplicationBuilder builder = Host.CreateApplicationBuilder();

builder.Services.AddSingleton<Program>();

using IHost host = builder.Build();
host.Run();

menu.ShowMenu();
phaseshift
phaseshift16mo ago
no, none of that is related validate not used. input not used. helpers not used. _context not used AddSingleton<Program> looks pointless
GIGA BRAIN
GIGA BRAINOP16mo ago
i think im just gonna have to come back to this later i have no idea what im doing
Philémon
Philémon16mo ago
i used something like this when i tried Console Dependency Injection. I dont know if its the perfect way to do DI in consoles, but i think its not that bad. You could use this example:
using IceCreamVendor.Core.Data;
using IceCreamVendor.Core.Logic;
using IceCreamVendor.Core.Service;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

IConfigurationBuilder builder = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: false);

IConfiguration config = builder.Build();

using IHost host = Host.CreateDefaultBuilder(args)
.ConfigureServices((_, services) => services
.AddTransient<IIceCreamService, IceCreamService>()
.AddTransient<ILogService, LogService>()
.AddTransient<ISellService, SellService>()
.AddDbContext<IceCreamContext>(options => options.UseSqlServer(config.GetConnectionString("DefaultConnection")))
.AddTransient<IceCreamBusiness>()
)
.Build();

RunBusiness(host.Services);

await host.RunAsync();

static void RunBusiness(IServiceProvider services)
{
using IServiceScope serviceScope = services.CreateScope();
IServiceProvider provider = serviceScope.ServiceProvider;

IceCreamBusiness business = provider.GetService<IceCreamBusiness>();
business?.RunBusiness();
}
}
using IceCreamVendor.Core.Data;
using IceCreamVendor.Core.Logic;
using IceCreamVendor.Core.Service;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

IConfigurationBuilder builder = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: false);

IConfiguration config = builder.Build();

using IHost host = Host.CreateDefaultBuilder(args)
.ConfigureServices((_, services) => services
.AddTransient<IIceCreamService, IceCreamService>()
.AddTransient<ILogService, LogService>()
.AddTransient<ISellService, SellService>()
.AddDbContext<IceCreamContext>(options => options.UseSqlServer(config.GetConnectionString("DefaultConnection")))
.AddTransient<IceCreamBusiness>()
)
.Build();

RunBusiness(host.Services);

await host.RunAsync();

static void RunBusiness(IServiceProvider services)
{
using IServiceScope serviceScope = services.CreateScope();
IServiceProvider provider = serviceScope.ServiceProvider;

IceCreamBusiness business = provider.GetService<IceCreamBusiness>();
business?.RunBusiness();
}
}
Philémon
Philémon16mo ago
I see that my repo is public, you can see it all at this address: https://github.com/PhilemonPhilippin/IceCreamVendor-repo/tree/master But i am junior dev and i made it at my even earlier stage, so take it with a grain of salt if i may say
GitHub
GitHub - PhilemonPhilippin/IceCreamVendor-repo: App to practice log...
App to practice loggings and DI. Contribute to PhilemonPhilippin/IceCreamVendor-repo development by creating an account on GitHub.
Philémon
Philémon16mo ago
The best thing to do in this case imo : watch a tutorial (youtube or else) on the subject, try it on your own, research more deeply for every bit you don't understand, and when you write code, F12 keyboard shortcut on every Microsoft base library that you don't understand will be your friend! You got this 💪
GIGA BRAIN
GIGA BRAINOP16mo ago
thanks for the input! Im gonna try and understand your code tomorrow and see how it goes 💯 and definitely for sure will try again with DI
Becquerel
Becquerel16mo ago
using PhoneBookApp;
using PhoneBookApp.Data;
using PhoneBookApp.Models;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

// Create the host and the container.
HostApplicationBuilder builder = Host.CreateApplicationBuilder();

// Tell the container what classes you want to use in DI.
builder.Services.AddSingleton<Menu>();
builder.Services.AddSingleton<Helpers>();
builder.Services.AddSingleton<UserInput>();
builder.Services.AddSingleton<Validate>();
builder.Services.AddTransient<PhoneBookContext>();

// Tell the container to figure out the web of dependencies for your classes.
// If your web of dependencies doesn't make sense it will throw an exception.
var host = builder.Build();

// Tell the container to give you an instance of a class you registered in DI.
var menu = host.Services.GetRequiredService<Menu>();

// Use the class to do stuff.
// The instance will have everything it asks for in its constructor automatically
// given to it by the DI container.
menu.ShowMenu();
using PhoneBookApp;
using PhoneBookApp.Data;
using PhoneBookApp.Models;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

// Create the host and the container.
HostApplicationBuilder builder = Host.CreateApplicationBuilder();

// Tell the container what classes you want to use in DI.
builder.Services.AddSingleton<Menu>();
builder.Services.AddSingleton<Helpers>();
builder.Services.AddSingleton<UserInput>();
builder.Services.AddSingleton<Validate>();
builder.Services.AddTransient<PhoneBookContext>();

// Tell the container to figure out the web of dependencies for your classes.
// If your web of dependencies doesn't make sense it will throw an exception.
var host = builder.Build();

// Tell the container to give you an instance of a class you registered in DI.
var menu = host.Services.GetRequiredService<Menu>();

// Use the class to do stuff.
// The instance will have everything it asks for in its constructor automatically
// given to it by the DI container.
menu.ShowMenu();
Using the host is different than using DI. Hosting in .NET lets you get other stuff, namely graceful shutdowns, built-in logging, etc., etc. But if you just want to learn DI then you can wire just that up like this.
GIGA BRAIN
GIGA BRAINOP16mo ago
wow thank you it was such a headache trying to understand how to set it up literally multiple days of no progress will try and understand this tomorrow 💯
Becquerel
Becquerel16mo ago
no prob. DI is confusing until it 'clicks' and then it becomes very simple feel free to message me if you want further explanations
GIGA BRAIN
GIGA BRAINOP16mo ago
sounds good, will do thank you again
Accord
Accord16mo 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.
Want results from more Discord servers?
Add your server