首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

C# 泛型實現資料庫無縫切換:MySQL ↔ SQL Server 零業務代碼修改

     編輯:關於C語言

前言

專案初期選用 MySQL,後期客戶要求改 SQL Server。利用 C# 泛型約束 `where T : IDbConnection, new()`,將資料存取層抽象為泛型類別,配合 `using` 別名指令與條件編譯,**僅修改 < 100 行基礎設施代碼,業務邏輯 SQL 完全不動**,實現資料庫無縫遷移。

核心泛型設計

1. 連線池泛型


public class ConnectionPool where T : IDbConnection, new()
{
    private readonly ConcurrentQueue _pool = new ConcurrentQueue();
    private readonly string _connectionString;
    private readonly int _maxSize;

    public ConnectionPool(string connStr, int maxSize = 20)
    {
        _connectionString = connStr;
        _maxSize = maxSize;
    }

    public T Acquire()
    {
        if (_pool.TryDequeue(out T conn)) return conn;
        conn = new T { ConnectionString = _connectionString };
        conn.Open();
        return conn;
    }

    public void Release(T conn)
    {
        if (conn.State == ConnectionState.Open && _pool.Count < _maxSize)
            _pool.Enqueue(conn);
        else
            conn.Dispose();
    }
}

2. 資料提供者泛型 (雙型別參數)


public class DataProvider 
    where TConn : IDbConnection, new()
    where TCmd : IDbCommand, new()
{
    private readonly ConnectionPool _pool;

    public DataProvider(string connStr) => _pool = new ConnectionPool(connStr);

    // 通用查詢
    public IEnumerable Query(string sql, Func map, params object[] parameters)
    {
        using var conn = _pool.Acquire();
        using var cmd = CreateCommand(conn, sql, parameters);
        using var reader = cmd.ExecuteReader();
        while (reader.Read()) yield return map(reader);
    }

    // 通用執行
    public int Execute(string sql, params object[] parameters)
    {
        using var conn = _pool.Acquire();
        using var cmd = CreateCommand(conn, sql, parameters);
        return cmd.ExecuteNonQuery();
    }

    // 純量查詢
    public object ExecuteScalar(string sql, params object[] parameters)
    {
        using var conn = _pool.Acquire();
        using var cmd = CreateCommand(conn, sql, parameters);
        return cmd.ExecuteScalar();
    }

    private TCmd CreateCommand(TConn conn, string sql, object[] parameters)
    {
        var cmd = new TCmd { Connection = conn, CommandText = sql };
        for (int i = 0; i < parameters.Length; i++)
        {
            var p = cmd.CreateParameter();
            p.ParameterName = $"@p{i}";
            p.Value = parameters[i] ?? DBNull.Value;
            cmd.Parameters.Add(p);
        }
        return cmd;
    }
}

使用別名與條件編譯切換

檔案頂部:DatabaseProvider.cs


#if MSSQLSERVER
using System.Data.SqlClient;
using DataProvider = MyProject.Data.DataProvider;
#elif MYSQL
using MySql.Data.MySqlClient;
using DataProvider = MyProject.Data.DataProvider;
#else
#error "請定義編譯符號 MSSQLSERVER 或 MYSQL"
#endif

專案屬性 → Build → 條件編譯符號

  • Debug/Release 兩組設定分別定義 `MSSQLSERVER` 或 `MYSQL`
  • 或建立兩個 Solution Configuration:`Debug_MySQL`、`Debug_SQLServer`

業務代碼完全無感


// UserRepository.cs - 完全不變
public class UserRepository
{
    private readonly DataProvider _dp; // 編譯時解析為具體型別

    public UserRepository(DataProvider dp) => _dp = dp;

    public User GetById(int id)
    {
        return _dp.Query("SELECT * FROM Users WHERE Id=@p0", r => new User
        {
            Id = r.GetInt32(0),
            Name = r.GetString(1),
            Email = r.GetString(2)
        }, id).FirstOrDefault();
    }

    public void Insert(User u)
    {
        _dp.Execute("INSERT INTO Users(Name,Email) VALUES(@p0,@p1)", u.Name, u.Email);
    }
}

進階:依賴注入註冊 (ASP.NET Core)


public void ConfigureServices(IServiceCollection services)
{
#if MSSQLSERVER
    services.AddScoped(sp => 
        new DataProvider(Configuration.GetConnectionString("SqlServer")));
#elif MYSQL
    services.AddScoped(sp => 
        new DataProvider(Configuration.GetConnectionString("MySQL")));
#endif

    services.AddScoped();
}

資料庫模型同步工具

  • Toad Data Modeler (免費版):ER 圖設計 → 生成 MySQL/SQL Server DDL → 切換目標平台自動調整型別(`AUTO_INCREMENT` ↔ `IDENTITY`、`DATETIME` ↔ `DATETIME2` 等)。
  • DbUp / FluentMigrator:版本化遷移腳本,跨資料庫執行。

開源工具鏈推薦

類別工具用途
IDEVS 2022 Community / VS Code + C# Dev Kit開發環境
版控Git + GitHub / GitLab原始碼管理
建模StarUML / Draw.ioUML/ER 圖
資料庫DBeaver / Azure Data Studio通用 GUI 管理
腳本PowerShell 7+自動化部署

限制與注意

  1. 標準 SQL 侷限:僅能使用 ANSI SQL 共同子集。`LIMIT`/`TOP`、`IFNULL`/`ISNULL`、`NOW()`/`GETDATE()` 等差異需自行抽象或避免使用。
  2. 參數前綴:SQL Server `@param`、MySQL `@param`/`?param`、Oracle `:param`。本例統一用 `@p{i}` 並由 `CreateParameter` 處理,實際驅動程式會自動適配。
  3. 大物件/資料流:`BLOB`/`TEXT` 讀寫 API 差異較大,建議封裝專用方法。
  4. 存儲過程:語法差異極大,**強烈建議避免使用**,改用參數化 SQL 或 ORM。

總結

**泛型 + 條件編譯 + 介面導向** 是 .NET Framework 時代跨資料庫的經典模式。現代 .NET Core/5+ 專案,**首選 Entity Framework Core**,其 Provider 模型(`UseMySQL`/`UseSqlServer`)在編譯期即解決方言差異,支援 LINQ、遷移、快取、追蹤等豐富功能,無需手寫泛型基礎設施。本模式適用於**無法引入 EF Core 的舊專案改造**、**極簡輕量專案**、**教學示範 ADO.NET 抽象原理**。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved