C# 中 SQLite 使用介紹
關於SQLite
SQLite是一款輕型的嵌入式的遵守ACID的關系型數據庫管理系統,誕生已有15個年頭了。隨著移動互聯的發展,現在得到了更廣泛的使用。
在使用SQLite之前,我們勢必要先了解它一些“個性”的地方。下面是它的一些特點:
1、 自包含。SQLite很大層度上是獨立的,他只需要非常小的外部庫支持。任何程序能夠訪問磁盤就可以使用SQLite數據庫。這使它適用於嵌入式設備,缺乏桌面計算機支持的基礎設施。這也使得SQLite適用於不作任何修改就可運行在不同配置電腦上的程序。
2、 無服務器。大多數SQL數據庫引擎被實現為一個單獨的服務器進程。程序要訪問數據庫與服務器通信使用某種形式的進程間通信(通常是TCP / IP),向服務器發送請求並接收返回結果。SQLite則不是這種工作方式。對於SQLite,想要訪問數據庫直接從磁盤上的對數據庫文件執行讀和寫操作。沒有中間的服務器進程。
3、 零配置。使用SQLite不需要“安裝”。沒有“設置”程序。沒有服務器進程需要啟動,停止,或配置。不需要管理員來創建一個新的數據庫實例或訪問權限分配給用戶。SQLite不使用配置文件。
4、 支持事務。事務數據庫的所有更改和查詢表現出原子性、一致性、隔離性、持久性(ACID)。執行SQLite的事務操作時,要麽完全執行,要麽不執行,即使寫入磁盤的操作被程序崩潰,斷電等故障打斷。
5、 開源。和前面的特點相比,這個似乎沒有多大關系。之所以把它作為一個特點,是因為開源在很大層度上會成為我們選擇一個解決方案的重要維度。
除了這些,SQLite還有著比同是開源的Mysql、PostgreSQL數據庫更快的處理效率,更低的資源占用。看起來很“完美”的東西背後往往也有著致命的缺陷。SQLite的缺陷雖不能說致命,但也足以讓你在選擇的過程中說NO。如果你要求更精準的控制數據庫訪問,細粒度鎖(SQLite只提供數據庫級的鎖定)以及很好的並發性(雖然可以手動實現並發,但是性能不高,也容易出現死鎖),SQLite也許不適合你。另外,SQLite也不適合遠程訪問,雖然可以通過網絡共享的方式運行,但是會存在文件鎖定的問題,而且訪問網絡共享相關的延遲會導致性能的下降。
安裝配置
前面說過了,使用SQLite是超級簡單的,無需安裝,只需要在官網下載庫文件添加引用即可。當然,還有很簡單的一些配置。還有更簡單的方法,使用Nuget添加SQLite庫文件並自動配置,這簡直是一鍵搞定啊。
首先,在Nuget中找到SQLite。這裏我們選擇第一個安裝。
會自動安裝如下庫。
安裝後會自動添加如下庫的引用。
並自動添加如下配置。
1 <?xml version="1.0" encoding="utf-8"?> 2 <configuration> 3 <configSections> 4 <!-- For more information on Entity Framework configuration, visit http://go.microsoft.com/fwlink/?LinkID=237468 --> 5 <section name="entityFramework" 6 type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" 7 requirePermission="false"/> 8 </configSections> 9 10 <entityFramework> 11 <defaultConnectionFactory type="System.Data.Entity.Infrastructure.LocalDbConnectionFactory, EntityFramework"> 12 <parameters> 13 <parameter value="v11.0"/> 14 </parameters> 15 </defaultConnectionFactory> 16 <providers> 17 <provider invariantName="System.Data.SqlClient" type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer"/> 18 <provider invariantName="System.Data.SQLite.EF6" type="System.Data.SQLite.EF6.SQLiteProviderServices, System.Data.SQLite.EF6"/> 19 </providers> 20 </entityFramework> 21 <system.data> 22 <DbProviderFactories> 23 <remove invariant="System.Data.SQLite.EF6"/><add name="SQLite Data Provider (Entity Framework 6)" invariant="System.Data.SQLite.EF6" 24 description=".NET Framework Data Provider for SQLite (Entity Framework 6)" type="System.Data.SQLite.EF6.SQLiteProviderFactory, System.Data.SQLite.EF6"/></DbProviderFactories> 25 </system.data></configuration>
SQLite的使用
上面的準備工作已經就緒。接下來開始使用SQLite。這裏不對如何創建數據庫作介紹,在實際工作中,我一般會選擇使用Navicat for SQLite來創建數據庫,方便快捷。如果有編碼創建數據庫要求,自行問度娘。如果要使用Linq,別忘了添加System.Data.Linq的引用。
1 /// <summary> 2 /// SQLite數據庫幫助類 3 /// </summary> 4 public class SQLiteHelper 5 { 6 /// <summary> 7 /// 數據庫路徑 8 /// </summary> 9 private static readonly string m_DataSource = ConfigurationManager.AppSettings["Test"]; 10 11 /// <summary> 12 /// ConnectionString樣例:Data Source=Test.db;Pooling=true;FailIfMissing=false 13 /// </summary> 14 private static readonly string m_ConnectionString; 15 16 /// <summary> 17 /// 靜態構造函數,初始化連接字符串,檢查數據庫連接 18 /// </summary> 19 static SQLiteHelper() 20 { 21 try 22 { 23 SQLiteConnectionStringBuilder connectionStringBuilder = new SQLiteConnectionStringBuilder 24 { 25 Version = 3, 26 Pooling = true, 27 FailIfMissing = false, 28 DataSource = m_DataSource 29 }; 30 m_ConnectionString = connectionStringBuilder.ConnectionString; 31 using (SQLiteConnection conn = new SQLiteConnection(m_ConnectionString)) 32 { 33 conn.Open(); 34 } 35 } 36 catch { } 37 } 38 39 #region basic method 40 41 /// <summary> 42 /// 獲得連接對象 43 /// </summary> 44 /// <returns></returns> 45 private static SQLiteConnection GetSQLiteConnection() 46 { 47 return new SQLiteConnection(m_ConnectionString); 48 } 49 50 /// <summary> 51 /// 預備命令 52 /// </summary> 53 /// <param name="cmd"></param> 54 /// <param name="conn"></param> 55 /// <param name="cmdText"></param> 56 /// <param name="commandParameters"></param> 57 private static void PrepareCommand(SQLiteCommand cmd, SQLiteConnection conn, string cmdText, params object[] commandParameters) 58 { 59 if (conn.State != ConnectionState.Open) 60 conn.Open(); 61 cmd.Parameters.Clear(); 62 cmd.Connection = conn; 63 cmd.CommandText = cmdText; 64 cmd.CommandType = CommandType.Text; 65 cmd.CommandTimeout = 30; 66 if (commandParameters != null) 67 { 68 foreach (object parm in commandParameters) 69 cmd.Parameters.AddWithValue(string.Empty, parm); 70 71 //for (int i = 0; i < p.Length; i++) 72 // cmd.Parameters[i].Value = p[i]; 73 } 74 } 75 76 /// <summary> 77 /// 返回受影響的行數 78 /// </summary> 79 /// <param name="cmdText">執行語句</param> 80 /// <param name="commandParameters">傳入的參數</param> 81 /// <returns>返回受影響行數</returns> 82 public static int ExecuteNonQuery(string cmdText, params object[] commandParameters) 83 { 84 SQLiteCommand command = new SQLiteCommand(); 85 using (SQLiteConnection connection = GetSQLiteConnection()) 86 { 87 PrepareCommand(command, connection, cmdText, commandParameters); 88 return command.ExecuteNonQuery(); 89 } 90 } 91 92 /// <summary> 93 /// 返回表集合 94 /// </summary> 95 /// <param name="cmdText">執行語句</param> 96 /// <param name="commandParameters">傳入的參數</param> 97 /// <returns>返回DataSet</returns> 98 public static DataSet ExecuteDataset(string cmdText, params object[] commandParameters) 99 { 100 DataSet ds = new DataSet(); 101 SQLiteCommand command = new SQLiteCommand(); 102 using (SQLiteConnection connection = GetSQLiteConnection()) 103 { 104 PrepareCommand(command, connection, cmdText, commandParameters); 105 SQLiteDataAdapter da = new SQLiteDataAdapter(command); 106 da.Fill(ds); 107 } 108 return ds; 109 } 110 111 /// <summary> 112 /// 返回SqlDataReader對象 113 /// </summary> 114 /// <param name="cmdText">執行語句</param> 115 /// <param name="commandParameters">傳入的參數</param> 116 /// <returns>返回SQLiteDataReader</returns> 117 public static SQLiteDataReader ExecuteReader(string cmdText, params object[] commandParameters) 118 { 119 SQLiteCommand command = new SQLiteCommand(); 120 SQLiteConnection connection = GetSQLiteConnection(); 121 try 122 { 123 PrepareCommand(command, connection, cmdText, commandParameters); 124 SQLiteDataReader reader = command.ExecuteReader(CommandBehavior.CloseConnection); 125 return reader; 126 } 127 catch 128 { 129 connection.Close(); 130 throw; 131 } 132 } 133 134 /// <summary> 135 /// 返回表第一行 136 /// </summary> 137 /// <param name="cmdText">執行語句</param> 138 /// <param name="commandParameters">傳入的參數</param> 139 /// <returns>返回:第一行</returns> 140 public static DataRow ExecuteDataRow(string cmdText, params object[] commandParameters) 141 { 142 DataSet ds = ExecuteDataset(cmdText, commandParameters); 143 if (ds != null && ds.Tables.Count > 0 && ds.Tables[0].Rows.Count > 0) 144 return ds.Tables[0].Rows[0]; 145 return null; 146 } 147 148 /// <summary> 149 /// 返回結果集中的第一行第一列,忽略其他行或列 150 /// </summary> 151 /// <param name="cmdText">執行語句</param> 152 /// <param name="commandParameters">傳入的參數</param> 153 /// <returns>返回:第一行第一列</returns> 154 public static object ExecuteScalar(string cmdText, params object[] commandParameters) 155 { 156 SQLiteCommand cmd = new SQLiteCommand(); 157 using (SQLiteConnection connection = GetSQLiteConnection()) 158 { 159 PrepareCommand(cmd, connection, cmdText, commandParameters); 160 return cmd.ExecuteScalar(); 161 } 162 } 163 164 #endregion 165 166 167 #region advanced method 168 169 /// <summary> 170 /// 獲取表所有數據 171 /// </summary> 172 /// <typeparam name="T">實體類型</typeparam> 173 /// <param name="tableName">表名</param> 174 /// <returns>表所有數據</returns> 175 public static List<T> GetTableData<T>(string tableName) where T : class 176 { 177 List<T> dataList = new List<T>(); 178 try 179 { 180 using (SqliteDataContext context = new SqliteDataContext(new SQLiteConnection(m_ConnectionString))) 181 { 182 string sql = "select * from " + tableName; 183 dataList = context.ExecuteQuery<T>(sql).ToList(); 184 context.SubmitChanges(); 185 } 186 } 187 catch { } 188 return dataList; 189 } 190 191 /// <summary> 192 /// 獲取表數據 193 /// </summary> 194 /// <typeparam name="T">實體類型</typeparam> 195 /// <param name="cmdText">sql語句</param> 196 /// <param name="parameter">參數</param> 197 /// <returns>表所有數據</returns> 198 public static List<T> GetTableData<T>(string cmdText, params object[] parameter) where T : class 199 { 200 List<T> dataList = new List<T>(); 201 try 202 { 203 using (SqliteDataContext context = new SqliteDataContext(new SQLiteConnection(m_ConnectionString))) 204 { 205 dataList = context.ExecuteQuery<T>(cmdText, parameter).ToList(); 206 } 207 } 208 catch {} 209 return dataList; 210 } 211 212 /// <summary> 213 /// 插入表數據 214 /// </summary> 215 /// <typeparam name="T">數據類型</typeparam> 216 /// <param name="tableName">表名</param> 217 /// <param name="dataList">數據集合</param> 218 /// <returns>true或false</returns> 219 public static bool BatchInsert<T>(string tableName, List<T> dataList) 220 { 221 try 222 { 223 if (dataList != null && dataList.Count > 0) 224 { 225 var temp = dataList[0]; 226 PropertyInfo[] propertyInfos = temp.GetType().GetProperties(); 227 List<string> propertyStrs = new List<string>(); 228 string propertyStr = ""; 229 foreach (var propertyInfo in propertyInfos) 230 { 231 propertyStrs.Add(propertyInfo.Name); 232 propertyStr = propertyStr + "@" + propertyInfo.Name + ","; 233 } 234 propertyStr = propertyStr.Remove(propertyStr.Length - 1); 235 236 using (SQLiteConnection conn = new SQLiteConnection(m_ConnectionString)) 237 { 238 using (SQLiteCommand command = new SQLiteCommand(conn)) 239 { 240 command.Connection.Open(); 241 using (SQLiteTransaction transaction = conn.BeginTransaction()) 242 { 243 command.Transaction = transaction; 244 command.CommandText = "insert into " + tableName + " values(" + propertyStr + ")"; 245 foreach (var needInsertData in dataList) 246 { 247 command.Parameters.Clear(); 248 for (int i = 0; i < propertyStrs.Count; i++) 249 { 250 command.Parameters.AddWithValue("@" + propertyStrs[i], propertyInfos[i].GetValue(needInsertData, null)); 251 } 252 command.ExecuteNonQuery(); 253 } 254 transaction.Commit(); 255 } 256 } 257 } 258 } 259 } 260 catch (Exception ex) 261 { 262 return false; 263 } 264 return true; 265 } 266 267 /// <summary> 268 /// 刪除表數據 269 /// </summary> 270 /// <param name="tableName">表名</param> 271 /// <returns>true或false</returns> 272 public static bool DeleteTableData(string tableName) 273 { 274 try 275 { 276 using (SQLiteConnection conn = new SQLiteConnection(m_ConnectionString)) 277 { 278 using (SQLiteCommand command = new SQLiteCommand(conn)) 279 { 280 command.Connection.Open(); 281 command.CommandText = "delete from " + tableName; 282 command.ExecuteNonQuery(); 283 } 284 } 285 } 286 catch (Exception ex) 287 { 288 return false; 289 } 290 return true; 291 } 292 293 #endregion 294 }SQLiteHelper
1 /// <summary> 2 /// Linq to SQLite 3 /// </summary> 4 public class SQLiteDataContext : DataContext 5 { 6 public SQLiteDataContext(string connection, MappingSource mappingSource) : 7 base(connection, mappingSource) 8 { 9 } 10 public SQLiteDataContext(IDbConnection connection, MappingSource mappingSource) : 11 base(connection, mappingSource) 12 { 13 } 14 public SQLiteDataContext(string connectionString) : 15 base(new SQLiteConnection(connectionString)) 16 { 17 } 18 public SQLiteDataContext(IDbConnection connection) : 19 base(connection) 20 { 21 } 22 }SQLiteDataContext
如果看了上面的Code,會發現查詢表數據方法是使用的Linq。這種方式相比遍歷IDataReader要高效得多。只是目前.NET只支持查詢,如果想要實現增刪改需要第三方庫支持。常見的有Dblinq 、linqconnect、linq2db和ado.net sqlite data provider等。不過使用事務批量插入數據也非常的快。
寫在後面
SQLite作為一款輕型高效的嵌入式數據庫,適合簡單高性能低並發的應用,在移動端使用將會是廣泛的,如果是高通信量的Web站點,SQLite是不合適的。
據@InkFx指出,也看了一些大數據案例,證實SQLite對高負載支持也很好(未親自測試)。當然,這種高負載也會受制於運行時的文件系統。
擴展閱讀:
http://blchen.com/sqlite-data-provider-in-visual-studio-2012/
C# 中 SQLite 使用介紹