C#访问数据库

计算机世界的技术最能体现“道生一,一生二,二生三,三生万物”的哲学观。从底层到高层,从简单到复杂,层层抽象、层层封装促进了技术的繁荣,但也掩盖了技术的本质。
在C#技术生态中,有许多数据库访问相关的技术,对这些技术进行关系梳理和适当总结很有必要,请看本文。

数据库访问技术概览

一些数据库相关技术的简称:

  • ODBC:Open Database Connectivity,开放数据库互连
  • OLEDB:Object Linking and Embedding, Database,对象连接嵌入数据库
  • ADO:ActiveX Data Object,活动数据对象

以下两种基本已被ADO和OLEDB技术替代:

  • DAO:Data Access Object,数据访问对象
  • RDO:Remote Data Objects,远程数据对象

应用程序可以通过三种方式访问数据库:

  • 应用程序——ODBC——数据库
  • 应用程序——OLEDB——ODBC——数据库
  • 应用程序——ADO——OLEDB——ODBC——数据库

ADO是非常成功的产品,但架构存在缺陷。因此微软进一步开发了ADO.NET,底层依然基于OLEDB技术,其架构示意如下:

ADO.NET

当前,众多ORM框架都是基于ADO.NET开发的,有必要对这种相对底层的数据库访问技术做一定了解。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
static void Main(string[] args) {
string connStr = "server=192.168.189.128;port=3306;uid=root;pwd=lizi710;database=testdb01;";
using (MySqlConnection conn = new MySqlConnection(connStr)) {
try {
conn.Open();
MySqlCommand cmd = new MySqlCommand() { Connection = conn };

// 查
string query = "SELECT * FROM T_Books";
cmd.CommandText = query;
using (MySqlDataReader reader = cmd.ExecuteReader()) {
while (reader.Read())
Console.WriteLine($"{reader["Name"]}\t{reader["Price"]}");
}

// 改
query = "UPDATE T_Books set Price='300' where Name='《中国哲学》'";
cmd.ExecuteNonQuery();

// 增
query = "INSERT INTO T_Books (Name,Price) VALUES (@value1,@value2)";
cmd.CommandText = query;
cmd.Parameters.Clear();
cmd.Parameters.AddWithValue("@value1", "《中国哲学》");
cmd.Parameters.AddWithValue("@value2", 160);
cmd.ExecuteNonQuery();

// 删
query = "DELETE FROM T_Books WHERE Price > 200";
cmd.CommandText = query;
cmd.ExecuteNonQuery();
}
catch (MySqlException ex) {
Console.WriteLine("Error: " + ex.Message);
}
Console.ReadKey();
}
}

Dapper

Dapper是一个轻量级ORM框架,能实现数据库和内存对象之间的数据流动,但是需要开发者自己编写SQL语句。

相比EFCore这种重量级的ORM框架,Dapper更具灵活性和高性能。

基本使用

  • 添加Dapper、Mysql.data的NuGet包
  • 在App.config中添加连接字符串
1
2
3
<connectionStrings>
<add name="testdb01" connectionString="Database=testdb01;Data Source=localhost;User Id=root;Password=lizi710;CharSet=utf8;port=3306" />
</connectionStrings>
  • 示例代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

class Program {
static void Main(string[] args) {
List<Person> res;
using (IDbConnection cnn = new MySqlConnection(LoadConnectString())) {
cnn.Open();
res = cnn.Query<Person>("select * from PERSON where age>=80").ToList();
}
Console.WriteLine("ID\t\tNAME\t\tAGE\t\tADDRESS\t\tSALARY");
foreach (var v in res)
Console.WriteLine($"{v.Id }\t\t{v.Name}\t\t{v.Age}\t\t{v.Address}\t\t{v.Salary}");
Console.ReadKey();
}

private static string LoadConnectString(string id = "testdb01") {
return ConfigurationManager.ConnectionStrings[id].ConnectionString;
}
}

public class Person {
public int Id { get; set; }
public string Name { get; set; }
public int Age { get; set; }
public string Address { get; set; }
public double Salary { get; set; }
}

工具类封装

增对增删改查等常规操作,封装若干泛型方法,以提高开发效率:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
public class DapperDBOperator {
private string dbPath;
public DapperDBOperator(string path) {
this.dbPath = path;
}

/// <summary>
/// 根据SQLite数据库的绝对路径获得连接字符串
/// </summary>
/// <param name="dbPath">数据库绝对路径</param>
/// <returns></returns>
public string GetConnectString(string dbPath) {
return "Data Source=" + dbPath;
}

/// <summary>
/// 从数据库中查询数据
/// </summary>
/// <typeparam name="T">与数据库中表对应的类</typeparam>
/// <param name="dbPath">数据库的绝对路径</param>
/// <param name="sql">查询字符串,示例:"select * from PERSON where age>=80"</param>
/// <returns></returns>
public List<T> QueryData<T>(string sql) {
List<T> res = null;
using (IDbConnection cnn = new SQLiteConnection(GetConnectString(dbPath))) {
cnn.Open();
res = cnn.Query<T>(sql, new DynamicParameters()).ToList();
}
return res;
}

/// <summary>
/// 从数据库中删除数据
/// </summary>
/// <param name="dbPath">数据库名绝对路径</param>
/// <param name="sql">执行删除的SQL语句,示例:"delete from PERSON where age>=70"</param>
/// <returns></returns>
public bool DeleteData(string sql) {
int res = 0;
using (IDbConnection cnn = new SQLiteConnection(GetConnectString(dbPath))) {
cnn.Open();
res = cnn.Execute(sql);
}
return res > 0;
}

/// <summary>
/// 向数据库中插入信息
/// </summary>
/// <typeparam name="T">与表对应的类</typeparam>
/// <param name="sql">插入信息的语句,示例:"insert into PERSON(id,name,age,address,salary) values(@Id,@Name,@Age,@Address,@Salary)"</param>
/// <param name="t">要插入的对象</param>
/// <returns></returns>
public bool InsertData<T>(string sql, T t) {
int res = 0;
using (IDbConnection cnn = new SQLiteConnection(GetConnectString(dbPath))) {
cnn.Open();
res = cnn.Execute(sql, t);
}
return res > 0;
}

/// <summary>
/// 向数据库中插入信息
/// </summary>
/// <typeparam name="T">与表对应的类</typeparam>
/// <param name="sql">更新SQL语句:"update PERSON set name=@Name,age=@Age,address=@Address,salary=@Salary where id=10000",</param>
/// <param name="t">要更新的信息</param>
/// <returns>成功返回true</returns>
public bool UpdateData<T>(string sql, T t) {
int res = 0;
using (IDbConnection cnn = new SQLiteConnection(GetConnectString(dbPath))) {
cnn.Open();
res = cnn.Execute(sql, t);
}
return res > 0;
}
}

使用方法如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
static void Main(string[] args) {
DapperDBOperator op = new DapperDBOperator(@"C:\testDB01.db");
//删
op.DeleteData("delete from PERSON where age>=60");
//查
var res = op.QueryData<Person>("select * from PERSON where age>=50");
foreach (var v in res)
Console.WriteLine($"{v.Id }\t\t{v.Name}\t\t{v.Age}\t\t{v.Address}");
//增
Person p = new Person();
p.Id = 100000;
p.Name = "lihao";
p.Age = 29;
p.Salary = 20000;
p.Address = "wuhanshi";
op.InsertData<Person>("insert into PERSON(id,name,age,address,salary) values(@Id,@Name,@Age,@Address,@Salary)", p);
//改
p.Salary = 40000;
op.UpdateData<Person>("update PERSON set name=@Name,age=@Age,address=@Address,salary=@Salary where id=100000", p);
Console.ReadKey();
}

public class Person {
public int Id { get; set; }
public string Name { get; set; }
public int Age { get; set; }
public string Address { get; set; }
public double Salary { get; set; }
}

Entity Framework

Entity Framework不仅仅是一个库,更是先进数据库设计方法的典范,功能极其强大,具体参看另一篇文章EFCore用法总结

评论