萬盛學電腦網

您现在的位置: 萬盛學電腦網 >> 程式設計 >  >> 網路程式設計 >> ASP.NET 程式設計

ASP.NET 列舉本機 SQL Server 實例:SqlDataSourceEnumerator 完整應用

核心 API

System.Data.Sql.SqlDataSourceEnumerator.Instance.GetDataSources() 返回包含可見 SQL Server 實例資訊的 DataTable。

返回資料結構

欄位名稱型別說明
ServerNamestring伺服器名稱 (機器名或 IP)
InstanceNamestring實例名稱 (預設實例為空/NULL)
IsClusteredbool是否叢集
Versionstring版本號 (如 15.0.2000.5 → SQL 2019)

完整實作範例

WebForms 頁面

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="SqlInstanceEnumerator.aspx.cs" Inherits="Demo.SqlInstanceEnumerator" %>
<html>
<head runat="server"><title>SQL Server 實例列舉</title></head>
<body>
    <form id="form1" runat="server">
        <h2>可用 SQL Server 實例</h2>
        <asp:DropDownList ID="ddlInstances" runat="server" Width="400px" 
            AutoPostBack="true" OnSelectedIndexChanged="ddlInstances_SelectedIndexChanged">
            <asp:ListItem Text="-- 請選擇實例 --" Value="" />
        </asp:DropDownList>
        <br /><br />
        <asp:Label ID="lblDetail" runat="server" ForeColor="Blue" />
    </form>
</body>
</html>

後端代碼

using System;
using System.Data;
using System.Data.Sql;

public partial class SqlInstanceEnumerator : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        if (!IsPostBack)
        {
            LoadSqlInstances();
        }
    }

    private void LoadSqlInstances()
    {
        try
        {
            DataTable instances = SqlDataSourceEnumerator.Instance.GetDataSources();
            
            foreach (DataRow row in instances.Rows)
            {
                string serverName = row["ServerName"]?.ToString() ?? "";
                string instanceName = row["InstanceName"]?.ToString() ?? "";
                string version = row["Version"]?.ToString() ?? "";
                bool isClustered = Convert.ToBoolean(row["IsClustered"]);
                
                string displayName = string.IsNullOrEmpty(instanceName) 
                    ? serverName 
                    : $"{serverName}\\{instanceName}";
                
                if (!string.IsNullOrEmpty(version))
                    displayName += $" (v{version})";
                if (isClustered)
                    displayName += " [Clustered]";
                
                ddlInstances.Items.Add(new ListItem(displayName, $"{serverName}|{instanceName}"));
            }
            
            if (ddlInstances.Items.Count == 1) // 只有預設項
            {
                ddlInstances.Items.Add(new ListItem("未偵測到實例 (可能被防火牆/瀏覽服務封鎖)", ""));
            }
        }
        catch (Exception ex)
        {
            lblDetail.Text = $"列舉失敗: {ex.Message}";
            // 記錄日誌
        }
    }

    protected void ddlInstances_SelectedIndexChanged(object sender, EventArgs e)
    {
        if (ddlInstances.SelectedIndex > 0)
        {
            string[] parts = ddlInstances.SelectedValue.Split('|');
            string server = parts[0];
            string instance = parts.Length > 1 ? parts[1] : "";
            
            lblDetail.Text = $"已選擇: {server} { (string.IsNullOrEmpty(instance) ? "(預設實例)" : $"\\{instance}") }";
            
            // 可進一步測試連線、建立連接字串等
            string connStr = string.IsNullOrEmpty(instance)
                ? $"Server={server};Database=master;Integrated Security=true;"
                : $"Server={server}\\{instance};Database=master;Integrated Security=true;";
            
            Session["SelectedConnStr"] = connStr;
        }
        else
        {
            lblDetail.Text = "";
        }
    }
}

進階:非同步列舉與快取

// 列舉可能耗時數秒,建議非同步 + 快取
private static readonly MemoryCache _cache = MemoryCache.Default;
private const string CacheKey = "SqlInstancesCache";

public async Task GetSqlInstancesAsync(bool forceRefresh = false)
{
    if (!forceRefresh && _cache.Contains(CacheKey))
        return (DataTable)_cache.Get(CacheKey);

    return await Task.Run(() => 
    {
        var dt = SqlDataSourceEnumerator.Instance.GetDataSources();
        _cache.Set(CacheKey, dt, DateTimeOffset.Now.AddMinutes(10)); // 快取 10 分鐘
        return dt;
    });
}

常見問題與限制

問題原因解決/替代
列舉結果為空SQL Browser 服務未啟動、防火牆封鎖 UDP 1434、網路隔離啟動 SQL Browser、開放 UDP 1434、或手動輸入連接字串
僅顯示本機實例跨網段廣播不通、域策略限制手動配置、Central Management Servers、註冊表掃描
權限不足異常應用程式池身分無列舉權限授予適當權限、或改用註冊表讀取 (HKLM\Software\Microsoft\Microsoft SQL Server)
.NET Core / .NET 5+ 不支援SqlDataSourceEnumerator 僅限 .NET Framework改用 `Microsoft.Data.SqlClient` 的 `SqlDataSourceEnumerator` (支援 .NET Core 3.1+)

.NET Core / .NET 5+ 現代寫法

// NuGet: Microsoft.Data.SqlClient
using Microsoft.Data.SqlClient;

var instances = SqlDataSourceEnumerator.Instance.GetDataSources();
// 相同 API,支援跨平台 (Windows/Linux/macOS)

替代方案:註冊表掃描 (本機安裝實例)

using Microsoft.Win32;

public static List GetLocalInstalledInstances()
{
    var instances = new List();
    using (var hklm = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64))
    using (var sqlKey = hklm.OpenSubKey(@"SOFTWARE\Microsoft\Microsoft SQL Server"))
    {
        if (sqlKey != null)
        {
            var installedInstances = sqlKey.GetValue("InstalledInstances") as string[];
            if (installedInstances != null)
            {
                foreach (var instance in installedInstances)
                {
                    instances.Add(instance.Equals("MSSQLSERVER", StringComparison.OrdinalIgnoreCase) 
                        ? Environment.MachineName 
                        : $"{Environment.MachineName}\\{instance}");
                }
            }
        }
    }
    return instances;
}

結語

`SqlDataSourceEnumerator` 適合「選擇連線目標」的安裝精靈/配置頁面。生產環境連線字串應預先配置於 Web.config/Key Vault/環境變數,**避免運行時動態發現**。理解其依賴 SQL Browser 服務與 UDP 1434 廣播機制,有助於排查「列舉為空」的網路/防火牆問題。

copyright © 萬盛學電腦網 all rights reserved