从零打造 C# 工业视觉检测系统(九):工厂模式实现 SQL Server / MySQL / SQLite 三库切换
系列标签:C# 工厂模式 ADO.NET SQL Server MySQL SQLite
本文目标:掌握可扩展的数据访问层设计,实现多种数据库的无缝切换
一、前言
工业软件的数据库选型受客户现场环境制约:
- 大型工厂已有 SQL Server 集群,要求数据统一入库
- 中小客户希望零成本部署,倾向 MySQL
- 单机设备或嵌入式场景,SQLite 是最轻量的选择
如果每个 SQL 语句都硬编码 SqlConnection,切换数据库将是一场噩梦。本文将详解本项目中的 工厂模式 + ADO.NET 架构。
二、IDatabaseProvider 接口设计
抽象出数据库操作的三个最基本动作:
public interface IDatabaseProvider
{
IDbConnection CreateConnection(string connectionString);
IDbCommand CreateCommand(string query, IDbConnection connection);
IDbDataAdapter CreateAdapter(IDbCommand command);
}
使用 IDbConnection、IDbCommand 等 ADO.NET 基接口,而非具体类型(如 SqlConnection),这是实现跨数据库的关键。
三、三种数据库的实现
3.1 SQL Server
public class SqlServerProvider : IDatabaseProvider
{
public IDbConnection CreateConnection(string connectionString)
{
return new SqlConnection(connectionString);
}
public IDbCommand CreateCommand(string query, IDbConnection connection)
{
return new SqlCommand(query, (SqlConnection)connection);
}
public IDbDataAdapter CreateAdapter(IDbCommand command)
{
return new SqlDataAdapter((SqlCommand)command);
}
}
3.2 MySQL
public class MySqlProvider : IDatabaseProvider
{
public IDbConnection CreateConnection(string connectionString)
{
return new MySqlConnection(connectionString);
}
public IDbCommand CreateCommand(string query, IDbConnection connection)
{
return new MySqlCommand(query, (MySqlConnection)connection);
}
public IDbDataAdapter CreateAdapter(IDbCommand command)
{
return new MySqlDataAdapter((MySqlCommand)command);
}
}
3.3 SQLite
public class SqliteProvider : IDatabaseProvider
{
public IDbConnection CreateConnection(string connectionString)
{
return new SQLiteConnection(connectionString);
}
public IDbCommand CreateCommand(string query, IDbConnection connection)
{
return new SQLiteCommand(query, (SQLiteConnection)connection);
}
public IDbDataAdapter CreateAdapter(IDbCommand command)
{
return new SQLiteDataAdapter((SQLiteCommand)command);
}
}
四、SqlHelper:统一入口
4.1 初始化与连接字符串构建
public static class SqlHelper
{
private static IDatabaseProvider _provider;
private static string _connectionString;
public static void Initialize(string dbType, string server,
string database, string user, string password)
{
DatabaseType type = (DatabaseType)Enum.Parse(typeof(DatabaseType), dbType);
_connectionString = BuildConnectionString(type, server, database, user, password);
switch (type)
{
case DatabaseType.SqlServer:
_provider = new SqlServerProvider();
break;
case DatabaseType.MySQL:
_provider = new MySqlProvider();
break;
case DatabaseType.SQLite:
_provider = new SqliteProvider();
break;
default:
throw new ArgumentException("Unsupported database type");
}
}
private static string BuildConnectionString(DatabaseType dbType, ...)
{
switch (dbType)
{
case DatabaseType.SqlServer:
return $"Server={server};Database={database};Uid={user};Password={password};";
case DatabaseType.MySQL:
return $"Server={server};Database={database};Uid={user};Pwd={password};";
case DatabaseType.SQLite:
return $"Data Source={database};Version=3;";
default:
throw new ArgumentException("Unsupported database type");
}
}
}
4.2 连接管理
private static IDbConnection OpenConnection()
{
_connection = _provider.CreateConnection(_connectionString);
if (_connection.State != ConnectionState.Open)
_connection.Open();
return _connection;
}
public static bool ConnectTest()
{
using (var conn = OpenConnection())
{
return _connection.State == ConnectionState.Open;
}
}
4.3 执行查询
public static DataTable GetDataTable(string sql)
{
DataTable dt = new DataTable();
try
{
using (var conn = OpenConnection())
{
using (var cmd = _provider.CreateCommand(sql, conn))
{
var adapter = _provider.CreateAdapter(cmd);
switch (_provider)
{
case SqlServerProvider _:
((SqlDataAdapter)adapter).Fill(dt);
break;
case MySqlProvider _:
((MySqlDataAdapter)adapter).Fill(dt);
break;
case SqliteProvider _:
((SQLiteDataAdapter)adapter).Fill(dt);
break;
}
}
}
}
catch (Exception ex)
{
MessageBox.Show("执行 SQL 异常: " + ex.ToString());
}
return dt;
}
4.4 执行非查询
public static int ExecuteNonQuery(string sql)
{
int rowsAffected = –1;
try
{
using (var conn = OpenConnection())
{
using (var cmd = _provider.CreateCommand(sql, conn))
{
rowsAffected = cmd.ExecuteNonQuery();
}
}
}
catch (Exception ex)
{
MessageBox.Show("执行 SQL 异常: " + ex.ToString());
}
return rowsAffected;
}
五、数据库表创建
CreateTableSql.cs 中定义了建表语句(SQL Server 语法):
public static string createProductSql =
"IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'dbo.Product')) " +
"BEGIN " +
"CREATE TABLE dbo.Product (" +
"id int identity(1,1) primary key NOT NULL, " +
"product_name nvarchar(50) NOT NULL, " +
"product_type int NOT NULL, " +
"product_length decimal(18,1) NOT NULL, " +
"length_up_offset decimal(18,1) NOT NULL, " +
"length_down_offset decimal(18,1) NOT NULL, " +
"product_width decimal(18,1) NOT NULL, " +
"width_up_offset decimal(18,1) NOT NULL, " +
"width_down_offset decimal(18,1) NOT NULL, " +
"delay_time decimal(18,1) NOT NULL, " +
"hold_time decimal(18,1) NOT NULL, " +
"create_time datetime, " +
"update_time datetime); " +
"END";
注意:SQLite 和 MySQL 的建表语法略有不同,实际项目中需要根据 dbType 切换对应的 SQL。
六、SQL 注入防范
6.1 当前项目的风险点
本项目为了简化,部分 SQL 采用字符串拼接:
string deleteSql = $"delete from Product where product_name = '{selectedLabel.Text}'";
如果 selectedLabel.Text 包含 ',将导致语法错误或注入攻击。
6.2 修复方案:参数化查询
public static int ExecuteNonQuerySafe(string sql, params IDbDataParameter[] parameters)
{
using (var conn = OpenConnection())
{
using (var cmd = _provider.CreateCommand(sql, conn))
{
foreach (var param in parameters)
{
cmd.Parameters.Add(param);
}
return cmd.ExecuteNonQuery();
}
}
}
// 调用示例
ExecuteNonQuerySafe(
"DELETE FROM Product WHERE product_name = @name",
new SqlParameter("@name", selectedLabel.Text));
七、总结
本文深入讲解了数据访问层的设计:
下篇预告:《从零打造 C# 工业视觉检测系统(十):检测记录入库与 Excel 导出实战》
我们将完成检测数据的保存、历史记录的分页查询,以及 EPPlus Excel 导出功能。
欢迎点赞、收藏、关注,你的支持是我持续输出的动力!
网硕互联帮助中心




评论前必须登录!
注册