You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

601 lines
22 KiB

4 years ago
4 years ago
3 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
4 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
4 years ago
4 years ago
3 years ago
4 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
4 years ago
4 years ago
3 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
4 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
3 years ago
4 years ago
4 years ago
4 years ago
3 years ago
4 years ago
4 years ago
4 years ago
4 years ago
3 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
3 years ago
4 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
4 years ago
4 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
3 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
3 years ago
  1. using Apewer.Internals;
  2. #if NETFRAMEWORK
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Data;
  6. using System.Data.OleDb;
  7. using System.IO;
  8. using System.IO.Compression;
  9. using System.Reflection;
  10. using System.Text;
  11. using static Apewer.Source.SourceUtility;
  12. #endif
  13. namespace Apewer.Source
  14. {
  15. /// <summary>连接 Access 数据库的客户端。</summary>
  16. public abstract partial class Access
  17. {
  18. /// <summary>尝试解析 Access 文件的密码。</summary>
  19. /// <remarks>当操作系统启用随机化内存分配时,此方法可能会产生异常。</remarks>
  20. public static string[] ParsePassword(string path) => AccessHelper.GetPassword(path);
  21. /// <summary>数据库文件路径。</summary>
  22. public virtual string Path { get; protected set; }
  23. }
  24. #if NETFRAMEWORK
  25. public abstract partial class Access : DbClient
  26. {
  27. #region 连接
  28. OleDbConnection _conn = null;
  29. string _connstr = null;
  30. /// <summary>构造函数。</summary>
  31. internal Access(string connectrionString, Timeout timeout = null) : base(timeout)
  32. {
  33. _connstr = connectrionString;
  34. }
  35. /// <summary>使用连接字符串创建数据库连接实例。</summary>
  36. /// <exception cref="ArgumentNullException"></exception>
  37. /// <exception cref="ArgumentException"></exception>
  38. public Access(IDbConnection connection, Timeout timeout = null) : base(timeout)
  39. {
  40. if (connection == null) throw new ArgumentNullException(nameof(connection), "指定的连接无效。");
  41. var conn = connection as OleDbConnection;
  42. if (conn == null) throw new ArgumentException(nameof(connection), "指定的连接不是支持的类型。");
  43. _conn = conn;
  44. _connstr = conn.ConnectionString;
  45. }
  46. #endregion
  47. #region public
  48. /// <summary></summary>
  49. public override string[] ColumnNames(string tableName)
  50. {
  51. if (_conn == null) _conn = new OleDbConnection(_connstr);
  52. // OleDbConnection.GetOleDbSchemaTable(OleDbSchemaGuid.Tables, new object[] { null, null, null, "TABLE" });
  53. var table = _conn.GetOleDbSchemaTable(OleDbSchemaGuid.Columns, new object[] { null, null, tableName.ToString(), null });
  54. using (var query = new Query(table))
  55. {
  56. var names = new ArrayBuilder<string>();
  57. for (var i = 0; i < query.Rows; i++)
  58. {
  59. names.Add(query.Text(i, "COLUMN_NAME"));
  60. }
  61. return names.Export();
  62. }
  63. }
  64. /// <summary></summary>
  65. public override string[] StoreNames() => throw new InvalidOperationException();
  66. /// <summary></summary>
  67. public override string[] TableNames()
  68. {
  69. // QueryStrings("select name from msysobjects where type=1 and flags = 0");
  70. Connect();
  71. var schema = _conn.GetSchema("Tables");
  72. using (var query = new Query(schema))
  73. {
  74. var json = query.ToJson();
  75. var names = new List<string>();
  76. for (var i = 0; i < query.Rows; i++)
  77. {
  78. var type = query.Text(i, "TABLE_TYPE");
  79. var name = query.Text(i, "TABLE_NAME");
  80. switch (type)
  81. {
  82. case "ACCESS TABLE":
  83. case "SYSTEM TABLE":
  84. break;
  85. case "TABLE":
  86. names.Add(name);
  87. break;
  88. }
  89. }
  90. names.Sort();
  91. return names.ToArray();
  92. }
  93. }
  94. /// <summary></summary>
  95. public override string Insert(object record, string table = null, bool adjust = true)
  96. {
  97. if (record == null) return "参数无效。";
  98. if (adjust) FixProperties(record);
  99. var structure = TableStructure.Parse(record.GetType());
  100. if (structure == null) return "无法解析记录模型。";
  101. if (string.IsNullOrEmpty(table)) table = structure.TableName;
  102. if (string.IsNullOrEmpty(table)) return "表名称无效。";
  103. // 排除字段。
  104. var excluded = new List<string>();
  105. foreach (var ca in structure.Columns)
  106. {
  107. // 排除不使用 ORM 的属性。
  108. if (ca.Independent || ca.Incremental) excluded.Add(ca.Field);
  109. }
  110. var ps = structure.CreateParameters(record, Parameter, excluded);
  111. var psc = ps.Length;
  112. if (psc < 1) return "数据模型不包含字段。";
  113. var names = new List<string>(psc);
  114. var values = new List<string>(psc);
  115. foreach (var column in ps)
  116. {
  117. //names.Add(TextGenerator.Merge("[", column, "]"));
  118. names.Add(TextUtility.Merge(column));
  119. values.Add("@" + column);
  120. }
  121. var sb = new StringBuilder();
  122. sb.Append("insert into ", table, "(", string.Join(", ", names.ToArray()), ") ");
  123. sb.Append("values(", string.Join(", ", values.ToArray()), "); ");
  124. var sql = sb.ToString();
  125. var execute = Execute(sql, ps, false);
  126. if (execute.Success) return TextUtility.Empty;
  127. return execute.Message;
  128. }
  129. /// <summary></summary>
  130. public override string Update(IRecord record, string table = null, bool adjust = true)
  131. {
  132. if (record == null) return "参数无效。";
  133. if (adjust)
  134. {
  135. FixProperties(record);
  136. SetUpdated(record);
  137. }
  138. var structure = TableStructure.Parse(record.GetType());
  139. if (structure == null) return "无法解析记录模型。";
  140. if (structure.Independent) return "无法更新带有 Independent 特性的模型。";
  141. if (string.IsNullOrEmpty(table)) table = structure.TableName;
  142. if (string.IsNullOrEmpty(table)) return "表名称无效。";
  143. // 排除字段。
  144. var excluded = new List<string>();
  145. if (structure.Key != null) excluded.Add(structure.Key.Field);
  146. foreach (var ca in structure.Columns)
  147. {
  148. // 排除不使用 ORM 的属性、自增属性和主键属性。
  149. if (ca.Independent || ca.Incremental || ca.PrimaryKey || ca.NoUpdate) excluded.Add(ca.Field);
  150. }
  151. var ps = structure.CreateParameters(record, Parameter, excluded);
  152. var psc = ps.Length;
  153. if (psc < 1) return "数据模型不包含字段。";
  154. var items = new List<string>();
  155. foreach (var p in ps)
  156. {
  157. var pn = p.ParameterName;
  158. items.Add(TextUtility.Merge("[", pn, "] = @", pn));
  159. }
  160. var key = record.Key.SafeKey();
  161. var sql = $"update {table} set {string.Join(", ", items.ToArray())} where [{structure.Key.Field}]='{key}'";
  162. var execute = Execute(sql, ps, false);
  163. if (execute.Success) return TextUtility.Empty;
  164. return execute.Message;
  165. }
  166. #endregion
  167. #region protected
  168. /// <summary></summary>
  169. public override string ConnectionString { get => _connstr; }
  170. /// <summary></summary>
  171. protected override IDbConnection GetConnection()
  172. {
  173. if (_conn == null) _conn = new OleDbConnection(_connstr);
  174. return _conn;
  175. }
  176. /// <summary></summary>
  177. protected override IDataAdapter CreateDataAdapter(IDbCommand command) => new OleDbDataAdapter((OleDbCommand)command);
  178. /// <summary></summary>
  179. protected override IDataParameter CreateParameter() => new OleDbParameter();
  180. /// <summary></summary>
  181. protected override string Initialize(TableStructure structure, string table)
  182. {
  183. var model = structure.Model;
  184. if (table.IsEmpty()) table = structure.TableName;
  185. // 检查现存表。
  186. var exists = false;
  187. var tables = TableNames();
  188. if (tables.Length > 0)
  189. {
  190. var lower = table.ToLower();
  191. foreach (var tn in tables)
  192. {
  193. if (TextUtility.IsEmpty(tn)) continue;
  194. if (tn.ToLower() == lower)
  195. {
  196. exists = true;
  197. break;
  198. }
  199. }
  200. }
  201. if (exists)
  202. {
  203. // 获取已存在的列名。
  204. var columns = ColumnNames(table);
  205. if (columns.Length > 0)
  206. {
  207. var lower = new List<string>();
  208. foreach (var column in columns)
  209. {
  210. if (TextUtility.IsBlank(column)) continue;
  211. lower.Add(column.ToLower());
  212. }
  213. columns = lower.ToArray();
  214. }
  215. // 增加列。
  216. foreach (var column in structure.Columns)
  217. {
  218. // 检查 Independent 特性。
  219. if (structure.Independent && column.Independent) continue;
  220. // 去重。
  221. var lower = column.Field.ToLower();
  222. if (columns.Contains(lower)) continue;
  223. var type = Declaration(column);
  224. if (string.IsNullOrEmpty(type)) return TextUtility.Merge("类型 ", column.Type.ToString(), " 不受支持。");
  225. var sql = TextUtility.Merge("alter table [", table, "] add ", type, "; ");
  226. var execute = Execute(sql, null, false);
  227. if (execute.Success == false) return execute.Message;
  228. }
  229. return TextUtility.Empty;
  230. }
  231. else
  232. {
  233. var sqlcolumns = new List<string>();
  234. foreach (var column in structure.Columns)
  235. {
  236. // 检查 Independent 特性。
  237. if (structure.Independent && column.Independent) continue;
  238. var type = Declaration(column);
  239. if (!column.Independent)
  240. {
  241. if (column.PrimaryKey) type = type + " primary key";
  242. if (column.Incremental) type += " identity";
  243. }
  244. if (string.IsNullOrEmpty(type)) return TextUtility.Merge("类型 ", column.Type.ToString(), " 不受支持。");
  245. sqlcolumns.Add(type);
  246. }
  247. if (sqlcolumns.Count < 1) return $"无法对类型 {model.FullName} 创建表:没有定义任何字段。";
  248. var sql = TextUtility.Merge("create table [", table, "](", string.Join(", ", sqlcolumns.ToArray()), "); ");
  249. var execute = Execute(sql, null, false);
  250. if (execute.Success) return TextUtility.Empty;
  251. return execute.Message;
  252. }
  253. }
  254. /// <summary></summary>
  255. protected override string Keys(string tableName, string keyField, string flagField, long flagValue)
  256. {
  257. if (flagValue == 0) return $"select [{keyField}] from [{tableName}]";
  258. else return $"select [{keyField}] from [{tableName}] where [{flagField}] = {flagValue}";
  259. }
  260. /// <summary></summary>
  261. protected override string Get(string tableName, string keyField, string keyValue, string flagField, long flagValue)
  262. {
  263. if (flagValue == 0) return $"select top 1 * from [{tableName}] where [{keyField}] = '{keyValue}'";
  264. else return $"select top 1 * from [{tableName}] where [{keyField}] = '{keyValue}' and [{flagField}] = {flagValue}";
  265. }
  266. /// <summary></summary>
  267. protected override string List(string tableName, string flagField, long flagValue)
  268. {
  269. if (flagValue == 0) return $"select * from [{tableName}]";
  270. else return $"select * from [{tableName}] where [{flagField}] = {flagValue}";
  271. }
  272. #endregion
  273. #region Parameter
  274. /// <summary>创建参数。</summary>
  275. /// <exception cref="ArgumentNullException"></exception>
  276. /// <exception cref="InvalidOperationException"></exception>
  277. static OleDbParameter Parameter(Parameter parameter)
  278. {
  279. if (parameter == null) throw new InvalidOperationException("参数无效。");
  280. return Parameter(parameter.Name, parameter.Type, parameter.Size, parameter.Value);
  281. }
  282. /// <summary>创建参数。</summary>
  283. public static OleDbParameter Parameter(string name, ColumnType type, int size, object value)
  284. {
  285. var vname = TextUtility.Trim(name);
  286. if (TextUtility.IsBlank(vname)) return null;
  287. var vtype = OleDbType.BigInt;
  288. switch (type)
  289. {
  290. case ColumnType.Boolean:
  291. vtype = OleDbType.Boolean;
  292. break;
  293. case ColumnType.Bytes:
  294. vtype = OleDbType.LongVarBinary;
  295. break;
  296. case ColumnType.Integer:
  297. vtype = OleDbType.Integer;
  298. break;
  299. case ColumnType.Float:
  300. vtype = OleDbType.Double;
  301. break;
  302. case ColumnType.DateTime:
  303. vtype = OleDbType.Date;
  304. break;
  305. case ColumnType.VarChar:
  306. case ColumnType.VarChar191:
  307. case ColumnType.VarCharMax:
  308. vtype = OleDbType.VarChar;
  309. break;
  310. case ColumnType.NVarChar:
  311. case ColumnType.NVarChar191:
  312. case ColumnType.NVarCharMax:
  313. vtype = OleDbType.VarWChar;
  314. break;
  315. case ColumnType.Text:
  316. vtype = OleDbType.LongVarChar;
  317. break;
  318. case ColumnType.NText:
  319. vtype = OleDbType.LongVarWChar;
  320. break;
  321. default:
  322. throw new InvalidOperationException(TextUtility.Merge("类型 ", type.ToString(), " 不受支持。"));
  323. }
  324. var vsize = size;
  325. switch (type)
  326. {
  327. case ColumnType.VarChar:
  328. vsize = NumberUtility.Restrict(vsize, 0, 8000);
  329. break;
  330. case ColumnType.NVarChar:
  331. vsize = NumberUtility.Restrict(vsize, 0, 4000);
  332. break;
  333. case ColumnType.VarChar191:
  334. case ColumnType.NVarChar191:
  335. vsize = NumberUtility.Restrict(vsize, 0, 191);
  336. break;
  337. default:
  338. vsize = 0;
  339. break;
  340. }
  341. var vvalue = value;
  342. if (vvalue is string && vvalue != null && vsize > 0)
  343. {
  344. vvalue = TextUtility.Left((string)vvalue, vsize);
  345. }
  346. var parameter = new OleDbParameter();
  347. parameter.ParameterName = vname;
  348. parameter.OleDbType = vtype;
  349. parameter.Value = vvalue;
  350. if (vsize > 0) parameter.Size = vsize;
  351. return parameter;
  352. }
  353. /// <summary>创建参数。</summary>
  354. public static IDbDataParameter CreateParameter(String field, DbType type, Int32 size, Object value)
  355. {
  356. var p = new OleDbParameter();
  357. p.ParameterName = field;
  358. p.DbType = type;
  359. p.Size = size;
  360. p.Value = value;
  361. return p;
  362. }
  363. /// <summary>创建参数。</summary>
  364. public static IDbDataParameter CreateParameter(String field, DbType type, Object value)
  365. {
  366. var p = new OleDbParameter();
  367. p.ParameterName = field;
  368. p.DbType = type;
  369. p.Value = value;
  370. return p;
  371. }
  372. #endregion
  373. #region protected
  374. /// <summary>获取或设置连接字符串。</summary>
  375. /// <exception cref="FileNotFoundException"></exception>
  376. internal protected static string GenerateCS(string provider, string path, string pass, string jo)
  377. {
  378. if (!File.Exists(path)) throw new FileNotFoundException("文件不存在。", path);
  379. var sb = new StringBuilder();
  380. sb.Append("provider=", provider, "; ");
  381. if (!string.IsNullOrEmpty(path)) sb.Append("data source=", path, "; ");
  382. if (string.IsNullOrEmpty(pass)) sb.Append("persist security info=false; ");
  383. else sb.Append("jet oledb:database password=\"", pass, "\"; ");
  384. // Microsoft Access Workgroup Information
  385. if (!string.IsNullOrEmpty(jo)) sb.Append("jet oledb:system database=", jo, "; ");
  386. return sb.ToString();
  387. }
  388. #endregion
  389. #region static
  390. static string Declaration(ColumnAttribute column)
  391. {
  392. var type = TextUtility.Empty;
  393. var vcolumn = column;
  394. var length = Math.Max(0, vcolumn.Length);
  395. switch (vcolumn.Type)
  396. {
  397. case ColumnType.Boolean:
  398. type = "bit";
  399. break;
  400. case ColumnType.Integer:
  401. type = "money";
  402. break;
  403. case ColumnType.Float:
  404. type = "float";
  405. break;
  406. case ColumnType.Bytes:
  407. type = "binary";
  408. break;
  409. case ColumnType.DateTime:
  410. type = "datetime";
  411. break;
  412. case ColumnType.VarChar:
  413. type = TextUtility.Merge("varchar(", Math.Min(8000, length).ToString(), ")");
  414. break;
  415. case ColumnType.VarChar191:
  416. type = TextUtility.Merge("varchar(191)");
  417. break;
  418. case ColumnType.VarCharMax:
  419. type = TextUtility.Merge("varchar(max)");
  420. break;
  421. case ColumnType.Text:
  422. type = TextUtility.Merge("text");
  423. break;
  424. case ColumnType.NVarChar:
  425. type = TextUtility.Merge("nvarchar(", Math.Min(4000, length).ToString(), ")");
  426. break;
  427. case ColumnType.NVarChar191:
  428. type = TextUtility.Merge("nvarchar(191)");
  429. break;
  430. case ColumnType.NVarCharMax:
  431. type = TextUtility.Merge("nvarchar(max)");
  432. break;
  433. case ColumnType.NText:
  434. type = TextUtility.Merge("ntext");
  435. break;
  436. default:
  437. return TextUtility.Empty;
  438. }
  439. return TextUtility.Merge("[", vcolumn.Field, "] ", type);
  440. }
  441. #endregion
  442. }
  443. /// <summary>使用 Microsoft.Jet.OLEDB.4.0 访问 Access 97 - 2003 数据库文件。</summary>
  444. public class AccessJet4 : Access
  445. {
  446. const string JetOleDB4 = "microsoft.jet.oledb.4.0";
  447. /// <summary>创建 Access 类的新实例。</summary>
  448. /// <exception cref="FileNotFoundException"></exception>
  449. public AccessJet4(string path, string pass = null, string jo = null, Timeout timeout = null)
  450. : base(GenerateCS(JetOleDB4, path, pass, jo), timeout)
  451. {
  452. Path = path;
  453. }
  454. /// <summary>使用连接字符串创建数据库连接实例。</summary>
  455. /// <exception cref="ArgumentNullException"></exception>
  456. /// <exception cref="ArgumentException"></exception>
  457. public AccessJet4(IDbConnection connection, Timeout timeout = null) : base(connection, timeout)
  458. {
  459. Path = (connection as Access)?.Path;
  460. }
  461. /// <summary>生成新的 Standard Jet DB 数据库。</summary>
  462. public static byte[] NewJetDB()
  463. {
  464. using (var resource = Assembly.GetExecutingAssembly().GetManifestResourceStream("Apewer.JETDB.gz"))
  465. {
  466. using (var gzip = new GZipStream(resource, CompressionMode.Decompress, false))
  467. {
  468. using (var memory = new MemoryStream())
  469. {
  470. BytesUtility.Read(gzip, memory, null, 1024);
  471. return memory.ToArray();
  472. }
  473. }
  474. }
  475. }
  476. }
  477. /// <summary>使用 Microsoft.ACE.OLEDB.12.0 访问 Access 2007 数据库文件。</summary>
  478. public class AccessAce12 : Access
  479. {
  480. const string AceOleDB12 = "microsoft.ace.oledb.12.0";
  481. /// <summary>创建 Access 类的新实例。</summary>
  482. /// <exception cref="FileNotFoundException"></exception>
  483. public AccessAce12(string path, string pass = null, string jo = null, Timeout timeout = null)
  484. : base(GenerateCS(AceOleDB12, path, pass, jo), timeout)
  485. {
  486. Path = path;
  487. }
  488. /// <summary>使用连接字符串创建数据库连接实例。</summary>
  489. /// <exception cref="ArgumentNullException"></exception>
  490. /// <exception cref="ArgumentException"></exception>
  491. public AccessAce12(IDbConnection connection, Timeout timeout = null) : base(connection, timeout)
  492. {
  493. Path = (connection as Access)?.Path;
  494. }
  495. /// <summary>生成新的 Standard ACE DB 数据库。</summary>
  496. public static byte[] NewAceDB()
  497. {
  498. using (var resource = Assembly.GetExecutingAssembly().GetManifestResourceStream("Apewer.ACEDB.gz"))
  499. {
  500. using (var gzip = new GZipStream(resource, CompressionMode.Decompress, false))
  501. {
  502. using (var memory = new MemoryStream())
  503. {
  504. BytesUtility.Read(gzip, memory, null, 1024);
  505. return memory.ToArray();
  506. }
  507. }
  508. }
  509. }
  510. }
  511. #endif
  512. }