云计算百科
云计算领域专业知识百科平台

从零打造 C# 工业视觉检测系统(九):工厂模式实现 SQL Server / MySQL / SQLite 三库切换

从零打造 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));


七、总结

本文深入讲解了数据访问层的设计:

  • IDatabaseProvider 接口:抽象三种数据库的共同操作
  • 工厂模式:SqlHelper.Initialize 根据配置动态创建对应 Provider
  • 连接管理:using 语句确保连接及时释放
  • SQL 注入:参数化查询是生产环境的必备措施
  • 下篇预告:《从零打造 C# 工业视觉检测系统(十):检测记录入库与 Excel 导出实战》

    我们将完成检测数据的保存、历史记录的分页查询,以及 EPPlus Excel 导出功能。


    欢迎点赞、收藏、关注,你的支持是我持续输出的动力!

    赞(0)
    未经允许不得转载:网硕互联帮助中心 » 从零打造 C# 工业视觉检测系统(九):工厂模式实现 SQL Server / MySQL / SQLite 三库切换
    分享到: 更多 (0)

    评论 抢沙发

    评论前必须登录!