-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathDbHelper.cs
More file actions
324 lines (297 loc) · 10.9 KB
/
Copy pathDbHelper.cs
File metadata and controls
324 lines (297 loc) · 10.9 KB
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
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Data.OleDb;
using System.Data.SQLite;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
namespace UltimateKtv;
public class DbHelper
{
public class Access
{
private static string OleDbProvider => Environment.Is64BitProcess ? "Microsoft.ACE.OLEDB.12.0" : "Microsoft.Jet.OLEDB.4.0";
public static OleDbConnection OpenConn(string Database, string? Password= null)
{
// Force the OLE DB provider to release any pooled connections.
// This can resolve "file already in use" errors by clearing lingering locks.
OleDbConnection.ReleaseObjectPool();
// Using a connection string builder is more robust and readable than manual concatenation.
var builder = new OleDbConnectionStringBuilder
{
Provider = OleDbProvider,
DataSource = Database
};
// Explicitly set the mode to allow shared read/write access. This often resolves "Operation must use an updateable query" errors.
builder["Mode"] = "Share Deny None";
if (!string.IsNullOrEmpty(Password))
{
builder["Jet OLEDB:Database Password"] = Password;
}
var connection = new OleDbConnection(builder.ConnectionString);
try
{
connection.Open();
}
catch (Exception ex)
{
// Log the error and re-throw to notify the caller of the failure.
// Swallowing exceptions here is dangerous and hides problems.
Console.WriteLine($"Failed to open Access connection: {ex.Message}");
connection.Dispose(); // Clean up the failed connection object.
throw;
}
return connection;
}
public static DataTable GetDataTable(string Database, string OleDbString, string? Password = null)
{
using OleDbConnection connection = OpenConn(Database, Password);
using OleDbDataAdapter oleDbDataAdapter = new OleDbDataAdapter(OleDbString, connection);
using DataSet dataSet = new DataSet();
oleDbDataAdapter.Fill(dataSet);
// Safely handle cases where the query returns no data tables to prevent an exception.
if (dataSet.Tables.Count > 0)
{
return dataSet.Tables[0];
}
// Return a new, empty DataTable if no data was returned.
return new DataTable();
}
public static List<Dictionary<string, object?>> GetDictionary(string Database, string OleDbString, string? Password = null)
{
using OleDbConnection selectConnection = OpenConn(Database, Password);
using OleDbDataAdapter oleDbDataAdapter = new OleDbDataAdapter(OleDbString, selectConnection);
using DataSet dataSet = new DataSet();
oleDbDataAdapter.Fill(dataSet);
// Safely handle cases where the query returns no data tables.
if (dataSet.Tables.Count > 0)
{
return dataSet.Tables[0].ToDictionary();
}
return new List<Dictionary<string, object?>>();
}
public static List<string> GetDBTableList(string Database, string? Password = null)
{
var tableList = new List<string>();
if (File.Exists(Database))
{
using OleDbConnection connection = OpenConn(Database, Password);
using DataTable dataTable = connection.GetSchema("Tables");
// The foreach loop handles an empty collection gracefully, so no need to check Rows.Count.
foreach (DataRow item in dataTable.AsEnumerable())
{
// Added null-conditional operator for safety and removed duplicate code.
if (item["TABLE_TYPE"]?.ToString() == "TABLE")
{
// Safely convert the table name to a string and add it to the list.
var tableName = item["TABLE_NAME"]?.ToString();
if (!string.IsNullOrEmpty(tableName))
{
tableList.Add(tableName);
}
}
}
}
return tableList;
}
public static List<string> GetDBColumnList(string Database, string TableName, string? Password = null)
{
var columnList = new List<string>();
using OleDbConnection connection = OpenConn(Database, Password);
// The restriction array must be declared as nullable (string?[]) to allow null elements.
using DataTable schema = connection.GetSchema("Columns", new string?[4] { null, null, TableName, null });
foreach (DataRow row in schema.Rows)
{
// Safely convert the column name to a string and add it to the list.
var columnName = row["COLUMN_NAME"]?.ToString();
if (!string.IsNullOrEmpty(columnName))
{
columnList.Add(columnName);
}
}
return columnList;
}
public static int ExecuteNonQuery(string Database, string OleDbString, string? Password = null, IEnumerable<OleDbParameter>? parameters = null)
{
// This method is for executing commands that don't return a result set (e.g., UPDATE, INSERT, DELETE).
using OleDbConnection connection = OpenConn(Database, Password);
using OleDbCommand command = new OleDbCommand(OleDbString, connection);
// Add parameters to the command to prevent SQL injection.
if (parameters != null)
{
foreach (var p in parameters)
{
// Ensure the parameter is not null before adding.
if (p != null)
{
command.Parameters.Add(p);
}
}
}
return command.ExecuteNonQuery();
}
public static void CompactAccessDB(string databasePath, string? password = null)
{
// This method uses the Jet and Replication Objects (JRO) library via COM Interop.
// It is fragile and requires the 32-bit Jet engine to be available.
string passwordSegment = !string.IsNullOrEmpty(password) ? $"Jet OLEDB:Database Password={password};" : "";
string sourceConnStr = $"Provider={OleDbProvider};Data Source={databasePath};{passwordSegment}";
string? tempDbPath = Path.ChangeExtension(databasePath, ".tmp");
if (string.IsNullOrEmpty(tempDbPath))
{
throw new ArgumentException("Invalid database path provided.", nameof(databasePath));
}
string destConnStr = $"Provider={OleDbProvider};Data Source={tempDbPath};Jet OLEDB:Engine Type=5";
object? jro = null;
try
{
// Late-bound call to the JRO COM object.
Type? jroType = Type.GetTypeFromProgID("JRO.JetEngine");
if (jroType is null)
{
throw new InvalidOperationException("Could not find the JRO.JetEngine COM object. Ensure the 32-bit Access Database Engine is installed.");
}
jro = Activator.CreateInstance(jroType);
if (jro is null)
{
throw new InvalidOperationException("Failed to create an instance of JRO.JetEngine.");
}
object[] args = new object[] { sourceConnStr, destConnStr };
jro.GetType().InvokeMember("CompactDatabase", BindingFlags.InvokeMethod, null, jro, args);
// If compacting succeeds, replace the original file.
File.Copy(tempDbPath, databasePath, overwrite: true);
}
catch (Exception ex)
{
// Log the error and re-throw. The caller needs to know that compacting failed.
Console.WriteLine($"Failed to compact Access database: {ex.Message}");
throw;
}
finally
{
// Ensure the temporary file is deleted and the COM object is released.
if (File.Exists(tempDbPath))
{
File.Delete(tempDbPath);
}
if (jro != null)
{
Marshal.ReleaseComObject(jro);
}
}
}
}
public class SQLite
{
public static SQLiteConnection OpenConn(string Database, string? Password = null)
{
// Using a connection string builder is a more robust and readable
// way to construct connection strings than manual concatenation.
var builder = new SQLiteConnectionStringBuilder
{
DataSource = Database,
Version = 3,
// Compress = true // This property may not exist in older package versions.
};
if (!string.IsNullOrEmpty(Password))
{
builder.Password = Password;
}
// Use the indexer to set the 'Compress' property for broader compatibility.
builder["Compress"] = true;
var connection = new SQLiteConnection(builder.ConnectionString);
try
{
connection.Open();
}
catch (Exception ex)
{
// It's better to let the caller handle the exception or log it,
// rather than swallowing it silently.
Console.WriteLine($"Failed to open SQLite connection: {ex.Message}");
connection.Dispose(); // Ensure connection is disposed on failure
throw;
}
return connection;
}
public static DataTable GetDataTable(string Database, string SQLString, string? Password = null)
{
using var connection = OpenConn(Database, Password);
using var adapter = new SQLiteDataAdapter(SQLString, connection);
using var dataSet = new DataSet();
adapter.Fill(dataSet);
// Safely handle cases where the query returns no data tables to prevent an exception.
if (dataSet.Tables.Count > 0)
{
return dataSet.Tables[0];
}
// Return a new, empty DataTable if no data was returned.
return new DataTable();
}
public static List<Dictionary<string, object?>> GetDictionary(string Database, string SQLString, string? Password = null)
{
using var connection = OpenConn(Database, Password);
using var adapter = new SQLiteDataAdapter(SQLString, connection);
using var dataSet = new DataSet();
adapter.Fill(dataSet);
// Safely handle cases where the query returns no data tables.
if (dataSet.Tables.Count > 0)
{
return dataSet.Tables[0].ToDictionary();
}
return new List<Dictionary<string, object?>>();
}
public static List<string> GetDBTableList(string Database, string? Password = null)
{
var tableList = new List<string>();
if (File.Exists(Database))
{
using var connection = OpenConn(Database, Password);
using DataTable schema = connection.GetSchema("Tables");
foreach (DataRow row in schema.Rows)
{
if (row["TABLE_TYPE"]?.ToString() == "TABLE")
{
// Safely convert the table name to a string and add it to the list.
var tableName = row["TABLE_NAME"]?.ToString();
if (!string.IsNullOrEmpty(tableName))
{
tableList.Add(tableName);
}
}
}
}
return tableList;
}
public static List<string> GetDBColumnList(string Database, string TableName, string? Password = null)
{
var columnList = new List<string>();
using var connection = OpenConn(Database, Password);
// The restriction array must be declared as nullable (string?[]) to allow null elements.
using DataTable schema = connection.GetSchema("Columns", new string?[4] { null, null, TableName, null });
foreach (DataRow row in schema.Rows)
{
// Safely convert the column name to a string and add it to the list.
var columnName = row["COLUMN_NAME"]?.ToString();
if (!string.IsNullOrEmpty(columnName))
{
columnList.Add(columnName);
}
}
return columnList;
}
public static void CompactSQLiteDB(string Database, string? Password = null)
{
if (!File.Exists(Database))
{
return;
}
using var connection = OpenConn(Database, Password);
using var command = connection.CreateCommand();
command.CommandText = "VACUUM;";
command.ExecuteNonQuery();
}
}
}