| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378 |
- using System;
- using System.Collections.Generic;
- using System.Data;
- using System.Data.SQLite;
- using System.IO;
- using System.Threading.Tasks;
- using TeamAAS.Database.Attributes;
- using TeamAAS.Database.Interfaces;
- using TeamAAS.Database.Models;
- namespace TeamAAS.Database.Providers
- {
- /// <summary>
- /// SQLite 数据库提供者实现
- /// </summary>
- [DatabaseProvider("sqlite", DisplayName = "SQLite", RequiresServer = false,
- Description = "SQLite 文件型数据库,基于 System.Data.SQLite")]
- public class SqliteDatabase : IDatabase
- {
- private SQLiteConnection _connection;
- private DatabaseConfig _config;
- private readonly object _sync = new object();
- public Guid Id { get; private set; }
- public string Name { get; set; }
- public string ProviderType => "sqlite";
- public bool IsConnected => _connection != null && _connection.State == ConnectionState.Open;
- public string ConnectionString
- {
- get
- {
- if (_config == null) return string.Empty;
- if (!string.IsNullOrWhiteSpace(_config.ConnectionString))
- return _config.ConnectionString;
- return $"Data Source={GetDbFilePath()};Version=3;";
- }
- }
- private string GetDbFilePath()
- {
- // SQLite: Server 字段存文件路径,或者 DatabaseName 存文件名
- if (!string.IsNullOrWhiteSpace(_config.Server) && _config.Server.Contains("\\"))
- return _config.Server;
- if (!string.IsNullOrWhiteSpace(_config.DatabaseName) && _config.DatabaseName.Contains("\\"))
- return _config.DatabaseName;
- var fileName = string.IsNullOrWhiteSpace(_config.DatabaseName)
- ? _config.Server
- : _config.DatabaseName;
- if (string.IsNullOrWhiteSpace(fileName))
- fileName = "database.db";
- if (!Path.IsPathRooted(fileName))
- fileName = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, fileName);
- return fileName;
- }
- private string GetRawConnectionString()
- {
- if (!string.IsNullOrWhiteSpace(_config?.ConnectionString))
- return _config.ConnectionString;
- if (_config == null)
- throw new InvalidOperationException("数据库未配置");
- var filePath = GetDbFilePath();
- var builder = new SQLiteConnectionStringBuilder
- {
- DataSource = filePath,
- Version = 3,
- FailIfMissing = false,
- JournalMode = SQLiteJournalModeEnum.Wal
- };
- if (_config.ConnectionTimeout > 0)
- builder.DefaultTimeout = _config.ConnectionTimeout;
- if (_config.ExtraParams != null)
- {
- foreach (var kv in _config.ExtraParams)
- {
- try
- {
- var prop = typeof(SQLiteConnectionStringBuilder).GetProperty(kv.Key);
- if (prop != null && prop.CanWrite)
- prop.SetValue(builder, Convert.ChangeType(kv.Value, prop.PropertyType));
- }
- catch { /* 忽略无效参数 */ }
- }
- }
- return builder.ConnectionString;
- }
- public void Configure(DatabaseConfig config)
- {
- _config = config ?? throw new ArgumentNullException(nameof(config));
- Id = config.Id == Guid.Empty ? Guid.NewGuid() : config.Id;
- Name = config.Name ?? $"SQLite_{Path.GetFileName(GetDbFilePath())}";
- }
- public bool Open()
- {
- lock (_sync)
- {
- try
- {
- if (_connection == null)
- {
- var filePath = GetDbFilePath();
- var dir = Path.GetDirectoryName(filePath);
- if (!string.IsNullOrWhiteSpace(dir) && !Directory.Exists(dir))
- Directory.CreateDirectory(dir);
- _connection = new SQLiteConnection(GetRawConnectionString());
- }
- if (_connection.State == ConnectionState.Open)
- return true;
- _connection.Open();
- return true;
- }
- catch
- {
- return false;
- }
- }
- }
- public Task<bool> OpenAsync()
- {
- return Task.Run(() => Open());
- }
- public void Close()
- {
- lock (_sync)
- {
- if (_connection != null)
- {
- try { _connection.Close(); } catch { }
- }
- }
- }
- public Task CloseAsync()
- {
- return Task.Run(() => Close());
- }
- public (bool Success, string Message) TestConnection()
- {
- try
- {
- using (var conn = new SQLiteConnection(GetRawConnectionString()))
- {
- conn.Open();
- return (true, "连接成功");
- }
- }
- catch (Exception ex)
- {
- return (false, ex.Message);
- }
- }
- public Task<(bool Success, string Message)> TestConnectionAsync()
- {
- return Task.Run(() => TestConnection());
- }
- public int ExecuteNonQuery(string sql, IDictionary<string, object> parameters = null)
- {
- using (var cmd = CreateCommand(sql, parameters))
- {
- EnsureOpen();
- return cmd.ExecuteNonQuery();
- }
- }
- public Task<int> ExecuteNonQueryAsync(string sql, IDictionary<string, object> parameters = null)
- {
- return Task.Run(() => ExecuteNonQuery(sql, parameters));
- }
- public DataTable ExecuteQuery(string sql, IDictionary<string, object> parameters = null)
- {
- using (var cmd = CreateCommand(sql, parameters))
- {
- EnsureOpen();
- var dt = new DataTable();
- using (var reader = cmd.ExecuteReader())
- {
- dt.Load(reader);
- }
- return dt;
- }
- }
- public Task<DataTable> ExecuteQueryAsync(string sql, IDictionary<string, object> parameters = null)
- {
- return Task.Run(() => ExecuteQuery(sql, parameters));
- }
- public object ExecuteScalar(string sql, IDictionary<string, object> parameters = null)
- {
- using (var cmd = CreateCommand(sql, parameters))
- {
- EnsureOpen();
- return cmd.ExecuteScalar();
- }
- }
- public Task<object> ExecuteScalarAsync(string sql, IDictionary<string, object> parameters = null)
- {
- return Task.Run(() => ExecuteScalar(sql, parameters));
- }
- public bool ExecuteTransaction(IEnumerable<string> sqlCommands)
- {
- if (sqlCommands == null) return true;
- EnsureOpen();
- using (var transaction = _connection.BeginTransaction())
- {
- try
- {
- foreach (var sql in sqlCommands)
- {
- if (string.IsNullOrWhiteSpace(sql)) continue;
- using (var cmd = _connection.CreateCommand())
- {
- cmd.Transaction = transaction;
- cmd.CommandText = sql;
- cmd.ExecuteNonQuery();
- }
- }
- transaction.Commit();
- return true;
- }
- catch
- {
- try { transaction.Rollback(); } catch { }
- return false;
- }
- }
- }
- public Task<bool> ExecuteTransactionAsync(IEnumerable<string> sqlCommands)
- {
- return Task.Run(() => ExecuteTransaction(sqlCommands));
- }
- public List<string> GetTableNames()
- {
- var tables = new List<string>();
- var sql = "SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%' ORDER BY name";
- using (var cmd = CreateCommand(sql, null))
- {
- EnsureOpen();
- using (var reader = cmd.ExecuteReader())
- {
- while (reader.Read())
- {
- tables.Add(reader.GetString(0));
- }
- }
- }
- return tables;
- }
- public Task<List<string>> GetTableNamesAsync()
- {
- return Task.Run(() => GetTableNames());
- }
- public List<ColumnInfo> GetTableSchema(string tableName)
- {
- var columns = new List<ColumnInfo>();
- var sql = $"PRAGMA table_info([{tableName}])";
- using (var cmd = CreateCommand(sql, null))
- {
- EnsureOpen();
- using (var reader = cmd.ExecuteReader())
- {
- while (reader.Read())
- {
- // cid, name, type, notnull, dflt_value, pk
- var colName = reader["name"].ToString();
- columns.Add(new ColumnInfo
- {
- ColumnName = colName,
- DataType = reader["type"].ToString(),
- IsNullable = Convert.ToInt32(reader["notnull"]) == 0,
- IsPrimaryKey = Convert.ToInt32(reader["pk"]) > 0,
- DefaultValue = reader["dflt_value"] == DBNull.Value ? null : reader["dflt_value"].ToString(),
- MaxLength = 0,
- Description = null
- });
- }
- }
- }
- return columns;
- }
- public Task<List<ColumnInfo>> GetTableSchemaAsync(string tableName)
- {
- return Task.Run(() => GetTableSchema(tableName));
- }
- #region 私有方法
- private SQLiteCommand CreateCommand(string sql, IDictionary<string, object> parameters)
- {
- var cmd = _connection.CreateCommand();
- cmd.CommandText = sql;
- if (parameters != null)
- {
- foreach (var kv in parameters)
- {
- var paramName = kv.Key.StartsWith("@") ? kv.Key : "@" + kv.Key;
- cmd.Parameters.AddWithValue(paramName, kv.Value ?? DBNull.Value);
- }
- }
- return cmd;
- }
- private void EnsureOpen()
- {
- if (_connection == null || _connection.State != ConnectionState.Open)
- {
- if (!Open())
- throw new InvalidOperationException("数据库连接失败");
- }
- }
- #endregion
- #region IDisposable
- private bool _disposed;
- public void Dispose()
- {
- Dispose(true);
- GC.SuppressFinalize(this);
- }
- protected virtual void Dispose(bool disposing)
- {
- if (_disposed) return;
- if (disposing)
- {
- Close();
- _connection?.Dispose();
- _connection = null;
- }
- _disposed = true;
- }
- ~SqliteDatabase()
- {
- Dispose(false);
- }
- #endregion
- }
- }
|