从 SQL 数据库中检索数据并使用动态创建的标签显示 Ping 结果

Retrieve data from SQL database and use dynamically created Labels to show Ping results

我需要在动态创建的标签中显示我的 SQL 数据库中的两个字段。因此,对于数据库中的每个条目,它都需要将其显示在彼此下方标签中的主要 window 上。但它没有显示标签中的信息。

所以这就是需要发生的事情。

  1. 用户将信息保存到 SQL 数据库(主机名、IP 地址和设备)

  2. 在主屏幕上(windows)然后需要这样显示:

    服务器 1 192.168.x.x“Ping 结果”

这是我目前拥有的代码(这还不包括 ping 结果标签)

private void AddDynamicLabels()
{
    string ConString = @"Data Source=PC\SQL;Initial Catalog=PingMonitorDB;Integrated Security=True";
    SqlConnection con = new SqlConnection(ConString);
    string CmdString = "SELECT host, ip FROM addhosts";
    SqlCommand cmd = new SqlCommand(CmdString, con);
    con.Open();
    SqlDataReader reader = cmd.ExecuteReader();
    while (reader.Read())
    {
        Label lbl = new Label();
        lbl.Visible = true;
        lbl.Text = reader["host" + "ip" + "   "].ToString();
        lbl.BackColor = System.Drawing.Color.Orange;
        this.Controls.Add(lbl);
    }
    con.Close();
}

因此,对于添加的每个条目,理论上应该如下所示:

 Server 1  192.168.x.x   "ping reply"
 Server 2  192.168.x.x   "ping reply"
 Server 3  192.168.x.x   "ping reply"
 ....
 etc

编辑

private async void frmMainWindow_Load(object sender, EventArgs e)
{
    SqlConnection cnn = new SqlConnection(@"Data Source=PC\SQL;Initial Catalog=PingMonitorDB;Integrated Security=True");
    cnn.Open();
    SqlCommand cmd = new SqlCommand();
    cmd.Connection = cnn;
    SqlDataAdapter dp = new SqlDataAdapter();

    DataTable dtTable = new DataTable();
    cmd.CommandText = "SELECT host, ip FROM addhosts";
    cmd.CommandType = CommandType.Text;
    dp.SelectCommand = cmd;
    dp.Fill(dtTable);
    cnn.Close();
    for (int i = 0; i < dtTable.Rows.Count; i++)
    {
        ipAddress.Add("ip");
        Label lbl = new Label();
        lbl.Visible = true;
        lbl.AutoSize = true;
        lbl.Font = new Font("Arial", 12, FontStyle.Bold);
        lbl.Text = dtTable.Rows[i]["host"].ToString() + "  " + dtTable.Rows[i]["ip"].ToString();
        lbl.BackColor = System.Drawing.Color.Orange;
        this.Controls.Add(lbl);
        flowLayoutPanel1.Controls.Add(lbl);

        var controls = new Control[] { /* a list of existing Controls */ };
        // The Addresses count must match the Controls'
        var addresses = ["0.0.0.0"]; => Not sure what to do here though?
        var massPing = new MassPing();
        await massPing.PingAll(addresses, controls, 2000);
    }
}

MassPing Class 显示在上一个问题中:

使用此代码:

SqlConnection cnn = new SqlConnection("....");
cnn.Open();
SqlCommand cmd = new SqlCommand();
cmd.Connection = cnn;
SqlDataAdapter dp = new SqlDataAdapter();

cmd.CommandText = "SELECT host, ip FROM addhosts";
cmd.CommandType = CommandType.Text;
dp.SelectCommand = cmd;
dp.Fill(dtTable);
cnn.Close();
for (int i = 0; i < dtTable.Rows.Count; i++)
{
    Label lbl = new Label();
    lbl.Visible = true;
    lbl.Text = dtTable.Rows[i]["host"].ToString() + dtTable.Rows[i]["ip"].ToString();
    lbl.BackColor = System.Drawing.Color.Orange;
    this.Controls.Add(lbl);
}

我已经修改了 MassPing Class 和 通用化 过程,以不同的方式创建了一个 Progress<T> 委托,在MassPing class.
SetPingProgressDelegate() 方法在调用 MassPing.PinAll() 方法之前创建委托。

  • 在您的代码中,每次读取新记录并以相同方式添加新控件时,您都会调用 PingAll() 方法。
    这当然行不通:您需要事先创建一个 IP 地址列表和一个控件列表,然后将创建的控件添加到 FlowLayoutPanel 并将地址列表传递给 MassPing 并等待其结果。

修改数据库访问过程以处理所有创建的一次性对象。
也在等待 SqlClient 提供的 async 方法。

  • 将调用代码移至 Shown 事件处理程序(而不是 Load)。
private async void frmMainWindow_Shown(object sender, EventArgs e)
{
    var ipAddresses = new List<string>();
    var ipControls = new List<Control>();
    var font = new Font("Segoe UI", 20, FontStyle.Regular, GraphicsUnit.Pixel);

    string sql = "SELECT [host], [ip] FROM addhosts";
    string connString = @"Data Source=PC\SQL;Initial Catalog=PingMonitorDB;Integrated Security=True";

    using (var conn = new SqlConnection(connString))
    using (var cmd = new SqlCommand(sql, conn)) {
        await conn.OpenAsync();
        using (var reader = await cmd.ExecuteReaderAsync()) {
            int ipCount = 0;
            while (await reader.ReadAsync()) {
                var lbl = new Label() {
                    AutoSize = false,
                    BackColor = Color.LightGray,
                    ForeColor = Color.Black,
                    Font = font,
                    Name = $"lblAddress{ipCount}",
                    Size = new Size(flowLayoutPanel1.ClientSize.Width, font.Height + 4),
                    Text = $"{reader["host"]} {reader["ip"]} "
                };
                ipAddresses.Add(reader["ip"].ToString());
                ipControls.Add(lbl);
            }
        }
    }

    // If the Reader returned some results, add the Control to a FlowLayoutPanel
    // and pass the list of addresses to MassPing, plus the Progress<T> delegate
    if (ipAddresses.Count > 0) {
        flowLayoutPanel1.Controls.AddRange(ipControls.ToArray());
        var massPing = new MassPing();
        var progress = SetPingProgressDelegate(ipControls);
        await massPing.PingAll(ipAddresses.ToArray(), progress, 2500);
    }
}

专业IProgress<T>委托:
在同一个表单中添加此方法。或者将访问修饰符更改为 public 并将其添加到表单可以到达的 internal class。
更改颜色以适合您设计的 UI。

private IProgress<(int, object)> SetPingProgressDelegate(List<Control> uiControls)
{
    var controls = uiControls;
    var obj = new object();

    // Create a Progress<T> delegate to receive status updates  
    // Each Task reports back here. This code is executed in the UI Thread
    var progress = new Progress<(int seq, object reply)>(report => {
        lock (obj) { // Not strictly necessary. Test without it
            var status = IPStatus.Unknown;
            var color = Color.LightGray;
            if (report.reply is PingReply pr) {
                status = pr.Status;
                color = status is IPStatus.Success
                    ? pr.RoundtripTime > 10 ? Color.Yellow : Color.LightGreen
                    : Color.OrangeRed;
            }
            else if (report.reply is SocketError socErr) {
                if (socErr == SocketError.HostNotFound) {
                    status = IPStatus.DestinationHostUnreachable;
                    color = Color.Red;
                }
                else {
                    // Something else went wrong. Handle if you care.
                    status = IPStatus.Unknown;
                }
            }
            controls[report.seq].BackColor = color;
            controls[report.seq].Text += status.ToString();
        }
    });
    return progress;
}

修改MassPingclass:
接收 IP 地址列表(作为字符串)并初始化相应数量的 PingAsync() 任务。每个 PingAsync() 任务都会报告其结果,包括创建任务时生成的序列指示符。请参阅注释:

using System.Collections.Generic;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Threading.Tasks;

public class MassPing
{
    public async Task PingAll(string[] addresses, IProgress<(int seq, object reply)> progress,  uint timeout = 2000)
    {
        var tasks = new List<Task>();

        // Add all tasks
        for (int seq = 0; seq < addresses.Length; seq++) {
            tasks.Add(PingAsync(addresses[seq], (int)timeout, seq, progress));
        }
        await Task.WhenAll(tasks);
    }

    private async Task PingAsync(string ipAddress, int timeOut, int sequence, IProgress<(int seq, object reply)> progress)
    {
        var buffer = new byte[32];
        var ping = new Ping();

        try {
            var options = new PingOptions(64, true);
            PingReply reply = await ping.SendPingAsync(ipAddress, timeOut, buffer, options);
            progress.Report((sequence, reply));
        }
        catch (PingException pex) {
            if (pex.InnerException is SocketException socEx) {
                progress.Report((sequence, socEx.SocketErrorCode));
            }
            else {
                // InnerException should be a SocketException. If not, something went bad
                // Handle specific cases if this kind of reporting is needed
                progress.Report((sequence, SocketError.SocketError));
            }
        }
        catch (InvalidOperationException) {
            // Overlapping sequence. Should never happen
            progress.Report((sequence, SocketError.AlreadyInProgress));
        }
        finally {
            ping.Dispose();
        }
    }
}