InstallDbCommand.cs 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Data;
  4. using System.Data.SqlClient;
  5. using System.Reflection;
  6. using BO.Console.Commands.Base;
  7. using Quadarax.Foundation.Core.Data.Extensions;
  8. using Quadarax.Foundation.Core.QConsole;
  9. using Quadarax.Foundation.Core.QConsole.Argument;
  10. using Quadarax.Foundation.Core.QConsole.Attributes;
  11. using Quadarax.Foundation.Core.QConsole.Value;
  12. using Quadarax.Foundation.Core.Resource.Extensions;
  13. using Quadarax.Foundation.Core.Value;
  14. namespace BO.Console.Commands.Install
  15. {
  16. [CommandDefinition]
  17. internal class InstallDbCommand : DbBaseCommand
  18. {
  19. #region *** Constants ***
  20. private const string DB_VERSION = "1.0.10";
  21. private const string DB_PROP_NAME = "BO_Version";
  22. private const string CS_CMD_INSTALL_NAME = "dbinstall";
  23. private const string CS_CMD_INSTALL_DESCR = "install BO database from scratch (supports SqlServer, LocalDB).";
  24. private const string ARG_PATH_CODE = "dpath";
  25. private const string ARG_PATH_HINT = "data_file_path";
  26. private const string ARG_PATH_DESC = "Absolute path to SQL Server datafile directory (ends without \\ character). Use '|' instead of ':'";
  27. private const string ARG_ISIZE_CODE = "isize";
  28. private const string ARG_ISIZE_HINT = "initial_size";
  29. private const string ARG_ISIZE_DESC = "Initial size in MB for Data and Log file";
  30. private const string ARG_GROWTH_CODE = "growth";
  31. private const string ARG_GROWTH_HINT = "growth";
  32. private const string ARG_GROWTH_DESC = "Data file growth in %";
  33. private const string ARG_FORCE_DESC = "Flag if defined override (if exists) existing database (drop and create) with loosing data";
  34. private const string VAL_LOCALDB = "(localdb)";
  35. private const string VAL_NOFILE = "nofile";
  36. #endregion
  37. #region *** Properties ***
  38. public override string Name => CS_CMD_INSTALL_NAME;
  39. public override string Description => CS_CMD_INSTALL_DESCR;
  40. public override string Notes => "For localDB (local db engine) use argument -srv:" + VAL_LOCALDB + "\\mssqllocaldb";
  41. private Version LatestVersion { get; }
  42. #endregion
  43. #region *** Constructor ***
  44. public InstallDbCommand(Engine engine) : base(engine)
  45. {
  46. LatestVersion = new Version(DB_VERSION);
  47. }
  48. #endregion
  49. #region *** EXECUTE ***
  50. protected override Result OnExecute()
  51. {
  52. var result = new Result();
  53. using (var conn = OpenMasterConnection())
  54. {
  55. if (!CheckIfDbExists(conn, DatabseName))
  56. {
  57. // database not exists
  58. CreateDatabase(conn);
  59. using (var connDb = OpenConnection())
  60. {
  61. UpdateModel(connDb);
  62. }
  63. }
  64. else
  65. {
  66. // database exists
  67. using (var connDb = OpenConnection())
  68. {
  69. var version = GetDbVersion(connDb);
  70. WriteCaption($"Database '{DatabseName}' exists (v.{version}).");
  71. }
  72. if (IsForce.GetValueOrDefault())
  73. {
  74. DeleteDatabase(conn);
  75. CreateDatabase(conn);
  76. using (var connDb = OpenConnection())
  77. {
  78. UpdateModel(connDb);
  79. }
  80. }
  81. else
  82. {
  83. WriteInfo($"Cancel. For force reinstall database use {Engine.Configuration.CharacterNamedArgumentSeparator}{CS_ARG_NAME_FORCE} flag.");
  84. }
  85. }
  86. }
  87. return result;
  88. }
  89. #endregion
  90. #region *** Private overrides ***
  91. protected override void OnValidateArguments()
  92. {
  93. if (ServerName.Contains(VAL_LOCALDB))
  94. {
  95. var pathArg = GetArgument(ARG_PATH_CODE);
  96. pathArg.Value.Set(VAL_NOFILE);
  97. }
  98. base.OnValidateArguments();
  99. }
  100. protected override IEnumerable<AbstractArgument> OnSetupArguments()
  101. {
  102. var arguments = new List<AbstractArgument>(base.OnSetupArguments());
  103. arguments.Add(new NamedArgument(ARG_PATH_CODE, ARG_PATH_DESC, ARG_PATH_HINT, TypeValuesEnum.String, string.Empty, true));
  104. arguments.Add(new NamedArgument(ARG_ISIZE_CODE, ARG_ISIZE_DESC, ARG_ISIZE_HINT, TypeValuesEnum.Integer, "10", false));
  105. arguments.Add(new NamedArgument(ARG_GROWTH_CODE, ARG_GROWTH_DESC, ARG_GROWTH_HINT, TypeValuesEnum.Integer, "10", false));
  106. AppendForceArgument(arguments,ARG_FORCE_DESC);
  107. return arguments;
  108. }
  109. #endregion
  110. #region *** Private Operations ***
  111. private void CreateDatabase(SqlConnection masterConnection)
  112. {
  113. WriteInfo($"Database '{DatabseName}' not exists creating ...");
  114. var dataPath = GetArgumentValueOrDefault<string>(ARG_PATH_CODE).Replace("|", ":");
  115. var initSize = GetArgumentValueOrDefault<int>(ARG_ISIZE_CODE);
  116. var growth = GetArgumentValueOrDefault<int>(ARG_GROWTH_CODE);
  117. var query = dataPath == VAL_NOFILE ? $"CREATE DATABASE {DatabseName} " : ($"CREATE DATABASE {DatabseName} ON PRIMARY " +
  118. $"(NAME = {DatabseName}_Data, " +
  119. $"FILENAME = '{dataPath}\\{DatabseName}.mdf', " +
  120. $"SIZE = {initSize}MB, FILEGROWTH = {growth}%) " +
  121. $"LOG ON (NAME = {DatabseName}_Log, " +
  122. $"FILENAME = '{dataPath}\\{DatabseName}.ldf', " +
  123. $"SIZE = {initSize}MB, " +
  124. $"FILEGROWTH = {growth}%)");
  125. WriteDebugInfo("Using query: " + query);
  126. var resultExecute = masterConnection.Execute(query);
  127. WriteDebugInfo($"Create script returns: {resultExecute}");
  128. if (resultExecute == 0)
  129. throw new Exception($"Database '{DatabseName}' creating FAILED.");
  130. WriteCaption($"Database '{DatabseName}' CREATED");
  131. }
  132. private void DeleteDatabase(SqlConnection masterConnection)
  133. {
  134. masterConnection.BatchExecute($"ALTER DATABASE [{DatabseName}] SET SINGLE_USER WITH ROLLBACK IMMEDIATE\nGO\nDROP DATABASE [{DatabseName}];\nGO");
  135. WriteInfo($"Database '{DatabseName}' dropped.");
  136. }
  137. private Version GetDbVersion(SqlConnection dbConnection)
  138. {
  139. var version = dbConnection.DatabasePropertyGet(DB_PROP_NAME);
  140. WriteDebugInfo($"DB version get: {version}");
  141. if (!string.IsNullOrEmpty(version))
  142. {
  143. return new Version(version);
  144. }
  145. return new Version();
  146. }
  147. private void SetDbVersion(SqlConnection dbConnection)
  148. {
  149. dbConnection.DatabasePropertyAddOrUpdate(DB_PROP_NAME,DB_VERSION);
  150. WriteDebugInfo($"DB version set: {DB_VERSION}");
  151. }
  152. private void UpdateModel(SqlConnection dbConnection)
  153. {
  154. var script = Assembly.GetExecutingAssembly().GetEmbeddedFileContentAsString("BO.Console.Scripts.bo_create_schema.sql");
  155. WriteCaption($"Creating latest DB structure in '{DatabseName}'");
  156. var resultExecute = dbConnection.BatchExecute(script);
  157. WriteDebugInfo($"Deploy script returns: {resultExecute}");
  158. if (resultExecute == 0)
  159. throw new Exception($"Database '{DatabseName}' deploying FAILED.");
  160. SetDbVersion(dbConnection);
  161. var version = GetDbVersion(dbConnection);
  162. WriteCaption($"Database '{DatabseName}' updated to version '{version}'");
  163. }
  164. #endregion
  165. }
  166. }