依赖项注入的类返回 Null(c#,Winforms 应用)

Dependency Injected Class Returns Null (c#,Winforms App)

提问人:October Sith 提问时间:7/4/2022 最后编辑:October Sith 更新时间:7/5/2022 访问量:183

问:

我不确定这里有什么问题,所以我正在联系。我有一个接口化的 DataLayer 类,它调用了我用来填充网格视图的一些客户数据。我将其直接注入到一个 winform 分部类中(我现在跳过 BusinessLayer)。我是 Winforms 的新手,所以也许我在这里没有看到一些东西,或者我的代码很密集。

接口:

namespace DL
{
    public interface IRepo<T>
    {
        List<T> GetAll();
    }
}

数据层

namespace DL
{
    public class CustomersRepo : IRepo<Customers>
    {
        string constring = ConfigurationManager.ConnectionStrings["ConString"].ConnectionString;
        
        public List<Customers> GetAll()
        {
            List<Customers> customers = new List<Customers>();
            string sqlQuery =@"SELECT * FROM CUSTOMERS";

            using (SqlConnection con = new SqlConnection(constring))
            {
                con.Open();
                SqlCommand command = new SqlCommand(sqlQuery, con);
                SqlDataReader reader = command.ExecuteReader();
                while (reader.Read())
                {
                    Customers customer = new Customers();
                    customer.CustomerID = reader.GetInt32(0);
                    customer.LastName = reader.GetString(1);
                    customer.FirstName = reader.GetString(2);
                    customer.Address = reader.GetString(3);
                    customer.City = reader.GetString(4);
                    customers.Add(customer);
                }
            }
            return customers;
        }
    }
}

依赖注入:

    public partial class Main : Form
    {
        public List<Customers> CustomersData { get; set; }

        private IRepo<Customers> _repo;
        public Main(IRepo<Customers> repo)
        {
            _repo = repo;
        }

主要:

        public Main()
        {
            CustomersData = _repo.GetAll();
            InitializeComponent();
        }

表单负载:

        private void Main_Load(object sender, EventArgs e)
        {
            dataGridViewCust.DataSource = CustomersData;
        }

如果我将此 Datalayer 方法直接插入到表单类中,则一切正常。我试图保持我在最近的训练营中学到的一些设计原则,因此试图通过依赖注入来分离关注点。我在某个地方傻了吗?我在这里查看了类似空问题的其他示例,但没有找到我的问题所在......任何帮助或建议将不胜感激。我觉得我在很大程度上忽略了一些东西。

编辑: (我也尝试在没有 IRepo 抽象的情况下直接插入 repo 类。

C# WinForms 依赖项注入 null nullreferenceexception

评论


答:

0赞 Taner Saydam 7/4/2022 #1

应在程序 .cs 中执行依赖项注入。为此,请安装 autofac 软件包并编写以下示例代码。

using Autofac;
using System;
using System.Windows.Forms;

namespace WindowsFormsApp1
{
    internal static class Program
    {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new Form1(Container.Resolve< IRepo>()));
        }

        static IContainer Configure()
        {
            var builder = new ContainerBuilder();
            builder.RegisterType<CustomersRepo>().As<IRepo>();
            builder.RegisterType<Form1>();
            return builder.Build();
        }
    }
 }

评论

0赞 Nkosi 7/4/2022
Form1 已添加到容器中,但仍手动初始化。为什么不通过容器解析 Form1。这样,在运行应用程序时,会根据需要注入所有内容
0赞 Taner Saydam 7/4/2022
我很久以前就写了这段代码。这就是为什么:)谢谢小费。
0赞 IV. 7/4/2022 #2

关于设置依赖注入,SO上有几个很好的答案(例如,在C#中的WinForms上使用Microsoft扩展依赖注入)。在您发布的代码中,还有一些其他问题需要解决,但首先应用该链接中的示例以用于创建和配置 .Microsoft.Extensions.DependencyInjectionIServiceProvider

static class Program
{
    [STAThread]
    static void Main()
    {
        Application.SetHighDpiMode(HighDpiMode.SystemAware);
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);

        // Set up DI here
        ConfigureServices();

        Application.Run(new Main());
    }
    public static IServiceProvider ServiceProvider { get; set; }

    static void ConfigureServices()
    {
        var services = new ServiceCollection();
        services.AddTransient<IRepo<Customer>, CustomersRepo>();
        ServiceProvider = services.BuildServiceProvider();
    }
}

Main 构造函数

没有必要将接口传递给 ctor,因为现在应该获取服务。MainProgram.ServiceProvider

public Main()
{
    InitializeComponent();
    _repo = 
        (IRepo<Customer>)
        Program.ServiceProvider.GetService(typeof(IRepo<Customer>));
}
private IRepo<Customer> _repo;

初始化 DataGridView

接下来,我们将设置一个模拟数据库查询进行初始化,如下所示:dataGridViewCust

datagridview

由于需要响应后续查询,因此它需要一个在客户列表发生更改时通知它。在此示例中,我已将其更改为表单事件的覆盖并对其进行初始化。DataGridViewDataSourceList<Customer>BindingList<Customer>LoadMain

/// <summary>
/// A binding list will notify DGV when data changes
/// </summary>
public BindingList<Customer> Customers { get; } = new BindingList<Customer>();
protected override void OnLoad(EventArgs e)
{
    base.OnLoad(e);
    dataGridViewCust.DataSource = Customers;

    // Here we're calling a method that simulates the
    // sql query by returning a fake list of customers
    foreach (var customer in _repo.GetAllMock())
    {
        Customers.Add(customer);
    }
    foreach (DataGridViewColumn column in dataGridViewCust.Columns)
    {
        column.AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill;
    }
}

使用 BindingList

您可能注意到 被声明为只读属性。执行新查询时,只需在添加新记录之前对其执行 a 即可。BindingListClear

private void changeTheBindingListExample()
{
    Customers.Clear();

    foreach (
        var customer in 
        _repo.NewQueryMock("SELECT * FROM CUSTOMERS WHERE FirstName='Other'"))
    {
        Customers.Add(customer);
    }
}

enter image description here

评论

0赞 IV. 7/4/2022
克隆此示例。