1

I am upgrading a .NET Framework Windows Form Application to .NET 5 using Dependency Injection (DI). There are forms that call other forms and pass in form properties as parameters. So, CustomerSummary calls CustomerDetail and passes in a CustomerId. The following demonstrated how to call one form from another:

https://marcominerva.wordpress.com/2020/03/09/using-hostbuilder-serviceprovider-and-dependency-injection-with-windows-forms-on-net-core-3/

But, it doesn't show how MainForm can pass a parameter to SecondForm. How can I pass a parameter from one form to another when using DI? Or, do I just create a new form and pass the values, along with the ServiceProvider, in the parameter list (this seems to defeat the purpose of DI)? The code is as follows:

CustomerSummary.cs

public partial class CustomerSummary : Form
{
    protected IConfiguration Configuration { get; set; }
    protected SomeDbNameEntities DbContext { get; set; }
    protected IServiceProvider ServiceProvider { get; set; }
    
    public CustomerSummary(IServiceProvider objServiceProvider)
    {
        this.Configuration = (IConfiguration)objServiceProvider.GetService(typeof(IConfiguration));

        this.DbContext = (SomeDbNameEntities)objServiceProvider.GetService(typeof(SomeDbNameEntities));

        this.ServiceProvider = (IServiceProvider)objServiceProvider.GetService(typeof(IServiceProvider));

        InitializeComponent();
        BindCustomerGrid();
    }

    private void dgvCustomer_RowHeaderMouseDoubleClick(object sender, DataGridViewCellMouseEventArgs e)
    {
        if ((dgvCustomer.SelectedRows[0].Cells[0].Value) != DBNull.Value)
        {
            int CustomerId = (int)dgvCustomer.SelectedRows[0].Cells[0].Value;

            // .NET Framework
            CustomerDetail chForm = new CustomerDetail(CustomerId); 

            // .NET 5. How do I pass the CustomerId?
            Form chForm = (CustomerDetail)ServiceProvider.GetService(typeof(CustomerDetail));

            chForm.Show();
        }
    }   
}   

CustomerDetail.cs

public partial class CustomerDetail : Form
{
    protected IConfiguration Configuration { get; set; }
    protected SomeDbNameEntities DbContext { get; set; }
    protected IServiceProvider ServiceProvider { get; set; }
    int BillToID;
    
    public CustomerDetail(int custid, IServiceProvider objServiceProvider)
    {
        this.Configuration = (IConfiguration)objServiceProvider.GetService(typeof(IConfiguration));

        this.DbContext = (SomeDbNameEntities)objServiceProvider.GetService(typeof(SomeDbNameEntities));

        this.ServiceProvider = (IServiceProvider)objServiceProvider.GetService(typeof(IServiceProvider));

        this.BillToID = custid;
        InitializeComponent();
    }
}

UPDATE The CustomerSummary form itself is called from another form (called MainMenu) with the following call:

Form chForm = (CustomerSummary)ServiceProvider.GetService(typeof(CustomerSummary)); 

chForm.Show()

This is why you see apparent anti-pattern behavior. Since you changed the CustomerSummary constructor (I basically added two in order to get the MainForm call to work), how would that change your solution because right now I get a build error relating to the fact that the wrong constructors are being called. Do I need to create a CustomerSummaryFactory?

J Weezy
  • 3,507
  • 3
  • 32
  • 88
  • I could be wrong but I don't think DI frameworks are going to allow you to inject by the int type. To do this with DI I think you'd have to make a class and then use a factory since custid is changing: https://stackoverflow.com/a/37765719/1429439. – C.M. Mar 19 '21 at 19:40

1 Answers1

4

The using of the ServiceProvider as a service locator is somewhat an anti-pattern. For example it's better to directly inject the needed dependencies into CustomerDetail.

public CustomerDetail(int custid, IConfiguration configuration, SomeDbNameEntities dbContext)
{
    this.Configuration = configuration;

    this.DbContext = dbContext;

    this.BillToID = custid;
    InitializeComponent();
}

As how to pass in custid. A common approach is the use of factories. It could look something like this

public class CustomerDetailFactory
{
    private IServiceProvider serviceProvider;
    
    public CustomerDetailFactory(IServiceProvider serviceProvider)
    {
        this.serviceProvider = serviceProvider;
    }
    
    public CustomerDetail Create(int custid)
    {
        var configuration = (IConfiguration)this.serviceProvider.GetService(typeof(IConfiguration));
        var dbContext = (SomeDbNameEntities)this.serviceProvider.GetService(typeof(SomeDbNameEntities));
        
        return new CustomerDetail(custid, configuration, dbContext);
    }
}

Then you use it in CustomerSummary

public partial class CustomerSummary : Form
{
    private CustomerDetailFactory customerDetailFactory;
    
    public CustomerSummary(CustomerDetailFactory customerDetailFactory)
    {
        this.customerDetailFactory = customerDetailFactory;

        InitializeComponent();
        BindCustomerGrid();
    }

    private void dgvCustomer_RowHeaderMouseDoubleClick(object sender, DataGridViewCellMouseEventArgs e)
    {
        if ((dgvCustomer.SelectedRows[0].Cells[0].Value) != DBNull.Value)
        {
            int CustomerId = (int)dgvCustomer.SelectedRows[0].Cells[0].Value;

            Form chForm = this.customerDetailFactory.Create(CustomerId);

            chForm.Show();
        }
    }   
}

Don't forget to register the factory as a service in Program.cs

Services.AddTransient<CustomerDetailFactory>();
J Weezy
  • 3,507
  • 3
  • 32
  • 88
treze
  • 3,159
  • 20
  • 21
  • Thank you for the comprehensive response. It appears that I left out some pertinent information by accident in an attempt to simplify the question as much as possible. Can you please review and advise? – J Weezy Mar 19 '21 at 20:52
  • Do you register the CustomerDetailFactory as a service? – treze Mar 19 '21 at 21:11
  • D'oh! I did now. I keep forgetting to add the service(s). Thanks! – J Weezy Mar 19 '21 at 21:37
  • Add-on question here https://stackoverflow.com/questions/66747976/net-5-windows-forms-with-dependency-injection-why-are-a-forms-textbox-and-fo – J Weezy Mar 22 '21 at 14:24