Home > Blockchain >  Initialize interfaces in main form with dependency injection
Initialize interfaces in main form with dependency injection

Time:02-26

This my code behind form ExpressionOfNeeds

private readonly IExpressionOfNeeds _expressionOfNeeds;
public FrmExpressionOfNeeds(IExpressionOfNeeds expressionOfNeeds)
{
    InitializeComponent();
    _expressionOfNeeds = expressionOfNeeds;
}
private async void FrmExpressionOfNeeds_Load(object sender, EventArgs e)
{
    GCData.DataSource = await _expressionOfNeeds.GetAllExpressionOfNeeds();
}

and this my code behind MainForm

private readonly IExpressionOfNeeds _expressionOfNeeds;
private readonly IService2 _service2;
private readonly IService3 _service3;
//and so on and so forth
public XtraMain()
{
    InitializeComponent();
}
private void bbtnExpressionOfNeeds_ItemClick(object sender, ItemClickEventArgs e)
{
    FrmExpressionOfNeeds frme = new(_expressionOfNeeds)
    {
        Name = "FrmExpressionOfNeeds"

    };
    ViewChildForm(frme);
}
private void bbtnForm2_ItemClick(object sender, ItemClickEventArgs e)
{
    Form2 frme = new(_service2)
    {
        Name = "Form2"

    };
    ViewChildForm(frme);
}
private void bbtnForm3_ItemClick(object sender, ItemClickEventArgs e)
{
    Form3 frme = new(_service3)
    {
        Name = "Form3"

    };
    ViewChildForm(frme);
}

and so on and so forth
and this is my code behind Program class

static void Main()
{
    var builder = new HostBuilder()
                 .ConfigureServices((hostContext, services) =>
                 {
                     services.AddScoped<XtraMain>();
                     services.AddSingleton<ISqlDataAccess, SqlDataAccess>();
                     services.AddSingleton<IExpressionOfNeeds, ExpressionOfNeeds>();
                 });
    var host = builder.Build();
    using (var serviceScope = host.Services.CreateScope())
    {
        var services = serviceScope.ServiceProvider;
        var mainform = services.GetRequiredService<XtraMain>();
        Application.Run(mainform);
    }
}

the problem is that the value of _expressionOfNeeds is always null and I can't find a way to Initialize it
Update
I have lots of forms and lots of Interfaces
I've only limited it to one example so the code isn't too big.

CodePudding user response:

I would suggest creating a factory to get the forms needed by the main form

public interface IFormFactory {
    TForm Create<TForm>() where TForm : Form;
}

assuming the types to be created in this case all derive from Form

the implementation will use the service provider to resolve provided form type

public class FormFactory: IFormFactory {
    private readonly IServiceProvider services;

    public FormFactory(IServiceProvider services) {
        this.services = services;
    }

    public TForm Create<TForm>() where TForm : Form {
        return services.GetRequiredService<TForm>();
    }
}

The main form will need to depend on the factory so that it can create the forms as needed

//...

private readonly IFormFactory factory;

public XtraMain(IFormFactory factory) {
    InitializeComponent();
    this.factory = factory;
}
private void bbtnExpressionOfNeeds_ItemClick(object sender, ItemClickEventArgs e) {
    FrmExpressionOfNeeds frme = factory.Create<FrmExpressionOfNeeds>();
    frme.Name = "FrmExpressionOfNeeds";
    ViewChildForm(frme);
}
private void bbtnForm2_ItemClick(object sender, ItemClickEventArgs e) {
    Form2 frme = factory.Create<Form2>();
    frme.Name = "Form2";
    ViewChildForm(frme);
}
private void bbtnForm3_ItemClick(object sender, ItemClickEventArgs e) {
    Form3 frme = factory.Create<Form3>();
    frme.Name = "Form3";
    ViewChildForm(frme);
}

//...

Make sure everything to be resolved by the service provider is registered at startup

static void Main() {
    var builder = new HostBuilder()
                 .ConfigureServices((hostContext, services) => {
                    services.AddScoped<XtraMain>();
                    services.AddTransient<FrmExpressionOfNeeds>();
                    services.AddTransient<Form2>();
                    services.AddTransient<Form3>();
                    services.AddSingleton<ISqlDataAccess, SqlDataAccess>();
                    services.AddSingleton<IExpressionOfNeeds, ExpressionOfNeeds>();
                    services.AddSingleton<IFormFactory, FormFactory>();
                 });
    var host = builder.Build();
    using (var serviceScope = host.Services.CreateScope()) {
        IServiceProvider services = serviceScope.ServiceProvider;
        XtraMain mainform = services.GetRequiredService<XtraMain>();
        Application.Run(mainform);
    }
}

That way all dependencies can be resolved and injected by the container regardless of how many forms and interfaces there are.

CodePudding user response:

You are almost there, but you have forgotten to inject the dependency to the constructor. You can find a step by step example in the following post:

Assuming you have a MainForm which has a dependency to ISomeServie, then you should have a constructor like this:

IMyService _myService;
public MainForm(IMyService myService)
{
    _myService = myService;
}

Then once you register MainForm and IMyService into the DI container and get the instance from service provider, everything will work as expected. Here is the main entry point:

static class Program
{
    public static IServiceProvider ServiceProvider { get; private set; }

    [STAThread]
    static void Main()
    {
        ApplicationConfiguration.Initialize();
        var host = CreateHostBuilder().Build();
        ServiceProvider = host.Services;
        Application.Run(ServiceProvider.GetRequiredService<MainForm>());
    }

    static IHostBuilder CreateHostBuilder()
    {
        return Host.CreateDefaultBuilder()
            .ConfigureServices((context, services)=>{
                services.AddTransient<IMyServiceService, MyService>();
                services.AddTransient<MainForm>();
            });
    }
}
  • Related