GPAL - Generally Positive Automation Library v1.0
GPAL The Fluent Automation LIbrary
Loading...
Searching...
No Matches
GPALDatabase.cs
1// =============================================================================
2// GPAL - Generally Positive Automation Library
3// Copyright © 2026 Software Decisions, Inc. All rights reserved.
4//
5// This file is part of GPAL.
6// Licensed under the Business Source License 1.1
7//
8// Primary development, architecture, and vision by Michael B. Vederman,
9// CEO of Software Decisions, Inc., Texas.
10//
11// Internal development maintained privately.
12// Public releases appear on GitHub: https://github.com/SoftwareDecisionsInc/GPAL.
13//
14// See LICENSE for full terms, including Additional Use Grant.
15// =============================================================================
16
17using System;
18using System.Collections.Generic;
19using System.Data.SqlClient;
20using System.Linq;
21using System.Text;
22using System.Threading.Tasks;
24using System.Data;
25using static GenerallyPositive.Enums;
26
27namespace GenerallyPositive
28{
33 public class GPALDatabase : IGPALDatabase, IAllowDatabaseGridSettings, IDisposable
34 {
35 private DatabaseSettings.DatabaseColumn lastDatabaseColumn { get; set; }
36 internal DatabaseSettings DatabaseSettings { get; set; }
37
38 public delegate int CallOnError(GPALDatabase database);
39
46 public class DatabaseError
47 {
49 public int Number { get; internal set; }
51 public string Message { get; internal set; }
53 public string Sql { get; internal set; }
55 public IGPALGrid<string> Parameters { get; internal set; }
57 public int Row { get; internal set; } = -1;
59 public List<string> RowValues { get; internal set; }
61 public DateTime Occurred { get; internal set; }
63 public Exception Exception { get; internal set; }
64 }
65
71 {
72 get
73 {
74 return DatabaseSettings.LastError;
75 }
76 internal set
77 {
78 DatabaseSettings.LastError = value;
79 }
80 }
81
82 public IGPALDatabase ToGPALObject()
83 {
84 return this;
85 }
86
87 internal GPALDatabase()
88 {
89 DatabaseSettings = new DatabaseSettings(this);
90 }
91
92 #region <Getters/Setters>
93 public IAllowParametersAndUsing Create
94 {
95 get
96 {
97 DatabaseSettings.SqlCommand = DatabaseSettings.CreateSQL;
98 return this;
99 }
100 }
101 public IAllowParametersAndInto Read
102 {
103 get
104 {
105 DatabaseSettings.SqlCommand = DatabaseSettings.ReadSQL;
106 return this;
107 }
108 }
109 public IAllowParametersAndUsing Update
110 {
111 get
112 {
113 DatabaseSettings.SqlCommand = DatabaseSettings.UpdateSQL;
114 return this;
115 }
116 }
117 public IAllowParametersAndUsing Delete
118 {
119 get
120 {
121 DatabaseSettings.SqlCommand = DatabaseSettings.DeleteSQL;
122 return this;
123 }
124 }
129 {
130 get
131 {
132 DatabaseSettings.ParametersList?.Clear();
133 DatabaseSettings.ParametersGrid?.Clear();
134
135 return this;
136 }
137 }
138
144 public IGPALGrid<string> Tokens
145 {
146 get
147 {
148 return DatabaseSettings.Tokens;
149 }
150 internal set
151 {
152 DatabaseSettings.Tokens = value;
153 }
154 }
155
156 public IAllowParametersAndActions BeginTransaction
157 {
158 get
159 {
160 if (null == DatabaseSettings.SqlTransaction)
161 {
162 try
163 {
164 DatabaseHelper.OpenConnection(DatabaseSettings);
165 DatabaseSettings.SqlTransaction = DatabaseSettings.Connection.BeginTransaction();
166 }
167 catch (Exception ex)
168 {
169 GPAL.PublishSimpleEvent(GPALEventType.EXCEPTION, "ERROR beginning transaction", this, Enums.GPALObjectType.Database, ex);
170 }
171 }
172 else
173 {
174 GPAL.PublishSimpleEvent(GPALEventType.WARNING, "Already in a transaction. You cannot start a new transaction while in a transaction. Doing nothing.", this, Enums.GPALObjectType.Database);
175 }
176 return this;
177 }
178 }
179
180 public IAllowDatabaseActions Rollback
181 {
182 get
183 {
184 DatabaseSettings.SqlTransaction?.Rollback();
185 DatabaseSettings.SqlTransaction = null;
186 return this;
187 }
188 }
189
190 public IAllowDatabaseActions Commit
191 {
192 get
193 {
194 DatabaseSettings.SqlTransaction?.Commit();
195 DatabaseSettings.SqlTransaction = null;
196 return this;
197 }
198 }
199
200 #endregion <Getters/Setters>
201 #region <Fluent Methods>
217 public IAllowDatabaseSettings WithConnectionString(string connectionString)
218 {
219 DatabaseSettings.ConnectionString = connectionString;
220 return this;
221 }
222
228 public IAllowDatabaseSettings WithUsername(string username)
229 {
230 DatabaseSettings.UserName = username;
231 return this;
232 }
233
239 public IAllowDatabaseSettings WithPassword(string password)
240 {
241 DatabaseSettings.Password = password;
242 return this;
243 }
244
249 public IAllowDatabaseSettings WithSQLCommand(string sqlCommand)
250 {
251 DatabaseSettings.SqlCommand = sqlCommand;
252 return this;
253 }
254
259 public IAllowDatabaseSettings WithStoredProcedure(string storedProcedure)
260 {
261 DatabaseSettings.StoredProcedure = storedProcedure;
262 return this;
263 }
264
270 public IAllowDatabaseSettings WithParameterName(string parameterName)
271 {
272 DatabaseSettings.ParameterNamesList.Add(parameterName);
273 return this;
274 }
275
281 {
282 // TODO: if datbase action already called, clear the parms
283 DatabaseSettings.ParametersList.Add(parameter);
284 return this;
285 }
286
291 public IAllowParametersAndActions WithParameterGrid(IGPALGrid<string> parametersGrid)
292 {
293 DatabaseSettings.ParametersGrid = parametersGrid;
294 return this;
295 }
296 // TODO: figure out fluent syntax for using the underlying sql
297 public IAllowDatabaseSettings WithCreateAs(string sqlCommand)
298 {
299 DatabaseSettings.CreateSQL = sqlCommand;
300 return this;
301 }
302 // TODO: figure out fluent syntax for using the underlying sql
303 public IAllowDatabaseSettings WithReadAs(string sqlCommand)
304 {
305 DatabaseSettings.ReadSQL = sqlCommand;
306 return this;
307 }
308 // TODO: figure out fluent syntax for using the underlying sql
309 public IAllowDatabaseSettings WithUpdateAs(string sqlCommand)
310 {
311 DatabaseSettings.UpdateSQL = sqlCommand;
312 return this;
313 }
314 // TODO: figure out fluent syntax for using the underlying sql
315 public IAllowDatabaseSettings WithDeleteAs(string sqlCommand)
316 {
317 DatabaseSettings.DeleteSQL = sqlCommand;
318 return this;
319 }
320
338 public IAllowDatabaseSettings WithDatabaseType(Enums.DatabaseType databaseType)
339 {
340 DatabaseSettings.DatabaseType = databaseType;
341 return this;
342 }
343
359 public IAllowDatabaseSettings WithServerName(string serverName)
360 {
361 DatabaseSettings.ServerName = serverName;
362 return this;
363 }
364
380 public IAllowDatabaseSettings WithDatabaseName(string databaseName)
381 {
382 DatabaseSettings.DatabaseName = databaseName;
383 return this;
384 }
385
402 public IAllowDatabaseSettings WithTableName(string tableName)
403 {
404 DatabaseSettings.TableName = tableName;
405 return this;
406 }
407
434 public IAllowDatabaseSettings WithCommandTimeout(int timeoutInSeconds)
435 {
436 DatabaseSettings.CommandTimeoutSeconds = timeoutInSeconds;
437 return this;
438 }
439
446 {
447 DatabaseHelper.CloseEverything(DatabaseSettings);
448 return this;
449 }
450
454 public void Dispose()
455 {
456 DatabaseHelper.CloseEverything(DatabaseSettings);
457 }
458
459 public IAllowDatabaseSettings WithRowCount(int rowCount)
460 {
461 DatabaseSettings.RowCount = rowCount;
462 return this;
463 }
464
465 #region <Output Column Settings [not yet implemented]>
471 public IAllowDatabaseSettings WithDatetimeParameter(string parameterName)
472 {
473 DatabaseSettings.DatabaseColumn databaseColumn = new DatabaseSettings.DatabaseColumn(parameterName, System.Data.SqlDbType.DateTime);
474 DatabaseSettings.ColumnList.Add(databaseColumn);
475 return this;
476 }
482 public IAllowDatabaseSettings WithTimeParameter(string parameterName)
483 {
484 DatabaseSettings.DatabaseColumn databaseColumn = new DatabaseSettings.DatabaseColumn(parameterName, System.Data.SqlDbType.Time);
485 DatabaseSettings.ColumnList.Add(databaseColumn);
486 return this;
487 }
493 public IAllowDatabaseSettings WithTimestampParameter(string parameterName)
494 {
495 DatabaseSettings.DatabaseColumn databaseColumn = new DatabaseSettings.DatabaseColumn(parameterName, System.Data.SqlDbType.Timestamp);
496 DatabaseSettings.ColumnList.Add(databaseColumn);
497 return this;
498 }
504 public IAllowPrecsionScaleAndSettings WithDecimalParameter(string parameterName)
505 {
506 DatabaseSettings.DatabaseColumn databaseColumn = new DatabaseSettings.DatabaseColumn(parameterName, System.Data.SqlDbType.Decimal);
507 DatabaseSettings.ColumnList.Add(databaseColumn);
508 return this;
509 }
515 public IAllowDatabaseSettings WithFloatParameter(string parameterName)
516 {
517 DatabaseSettings.DatabaseColumn databaseColumn = new DatabaseSettings.DatabaseColumn(parameterName, System.Data.SqlDbType.Float);
518 DatabaseSettings.ColumnList.Add(databaseColumn);
519 return this;
520 }
526 public IAllowDatabaseSettings WithImageParameter(string parameterName)
527 {
528 DatabaseSettings.DatabaseColumn databaseColumn = new DatabaseSettings.DatabaseColumn(parameterName, System.Data.SqlDbType.Image);
529 DatabaseSettings.ColumnList.Add(databaseColumn);
530 return this;
531 }
537 public IAllowDatabaseSettings WithIntegerParameter(string parameterName)
538 {
539 DatabaseSettings.DatabaseColumn databaseColumn = new DatabaseSettings.DatabaseColumn(parameterName, System.Data.SqlDbType.Int);
540 DatabaseSettings.ColumnList.Add(databaseColumn);
541 return this;
542 }
548 public IAllowDatabaseSettings WithMoneyParameter(string parameterName)
549 {
550 DatabaseSettings.DatabaseColumn databaseColumn = new DatabaseSettings.DatabaseColumn(parameterName, System.Data.SqlDbType.Money);
551 DatabaseSettings.ColumnList.Add(databaseColumn);
552 return this;
553 }
559 public IAllowParameterLength WithCharParameter(string parameterName)
560 {
561 DatabaseSettings.DatabaseColumn databaseColumn = new DatabaseSettings.DatabaseColumn(parameterName, System.Data.SqlDbType.Char);
562 lastDatabaseColumn = databaseColumn;
563 DatabaseSettings.ColumnList.Add(databaseColumn);
564 return this;
565 }
571 public IAllowParameterLength WithNCharParameter(string parameterName)
572 {
573 DatabaseSettings.DatabaseColumn databaseColumn = new DatabaseSettings.DatabaseColumn(parameterName, System.Data.SqlDbType.NChar);
574 lastDatabaseColumn = databaseColumn;
575 DatabaseSettings.ColumnList.Add(databaseColumn);
576 return this;
577 }
583 public IAllowParameterLength WithVarCharParameter(string parameterName)
584 {
585 DatabaseSettings.DatabaseColumn databaseColumn = new DatabaseSettings.DatabaseColumn(parameterName, System.Data.SqlDbType.VarChar);
586 lastDatabaseColumn = databaseColumn;
587 DatabaseSettings.ColumnList.Add(databaseColumn);
588 return this;
589 }
595 public IAllowParameterLength WithNVarCharParameter(string parameterName)
596 {
597 DatabaseSettings.DatabaseColumn databaseColumn = new DatabaseSettings.DatabaseColumn(parameterName, System.Data.SqlDbType.NVarChar);
598 lastDatabaseColumn = databaseColumn;
599 DatabaseSettings.ColumnList.Add(databaseColumn);
600 return this;
601 }
607 public IAllowParameterLength WithTextParameter(string parameterName)
608 {
609 DatabaseSettings.DatabaseColumn databaseColumn = new DatabaseSettings.DatabaseColumn(parameterName, System.Data.SqlDbType.Text);
610 lastDatabaseColumn = databaseColumn;
611 DatabaseSettings.ColumnList.Add(databaseColumn);
612 return this;
613 }
619 public IAllowParameterLength WithNTextParameter(string parameterName)
620 {
621 DatabaseSettings.DatabaseColumn databaseColumn = new DatabaseSettings.DatabaseColumn(parameterName, System.Data.SqlDbType.NText);
622 lastDatabaseColumn = databaseColumn;
623 DatabaseSettings.ColumnList.Add(databaseColumn);
624 return this;
625 }
626 public IAllowDatabaseSettings WithParameterLength(int length)
627 {
628 lastDatabaseColumn.ColumnLength = length;
629 return this;
630 }
631
632 public IAllowPrecsionScaleAndSettings WithParameterPrecision(int columnPrecision)
633 {
634 lastDatabaseColumn.ColumnPrecision = columnPrecision;
635 return this;
636 }
637
638 public IAllowPrecsionScaleAndSettings WithParameterScale(int columnScale)
639 {
640 lastDatabaseColumn.ColumnScale = columnScale;
641 return this;
642 }
643
644 public IAllowDatabaseSettings WithTableParameter(string parameterName)
645 {
646 DatabaseSettings.DatabaseColumn databaseColumn = new DatabaseSettings.DatabaseColumn(parameterName, SqlDbType.Structured);
647 lastDatabaseColumn = databaseColumn;
648 DatabaseSettings.ColumnList.Add(databaseColumn);
649 return this;
650 }
651 public IAllowDatabaseSettings WithUDTTName(string typeName)
652 {
653 if (lastDatabaseColumn == null)
654 {
655 GPAL.PublishSimpleEvent(GPALEventType.WARNING,
656 "No column defined before setting UDTT name. Call .WithTableParameter() first.",
657 this, GPALObjectType.Database);
658 return this;
659 }
660 if (lastDatabaseColumn.ColumnType != SqlDbType.Structured)
661 {
662 GPAL.PublishSimpleEvent(GPALEventType.WARNING,
663 $"UDTT name specified for non-Structured column [{lastDatabaseColumn.ColumName}]. Ignoring.",
664 this, GPALObjectType.Database);
665 }
666 lastDatabaseColumn.TvTableTypeName = typeName;
667 return this;
668 }
669 public IAllowDatabaseExecute Using(IGPALGrid<string> parametersGrid)
670 {
671 DatabaseSettings.ParametersGrid = parametersGrid;
672 return this;
673 }
674
675 public IAllowDatabaseExecute Using(DataTable dataTable)
676 {
677 IGPALGrid<string> parametersGrid = DatabaseHelper.DataTableToGrid(dataTable, out List<DatabaseSettings.DatabaseColumn> columns);
678 DatabaseSettings.ParametersGrid = parametersGrid;
679 DatabaseSettings.ColumnList = columns;
680 return this;
681 }
682
683 public IAllowDatabaseGridSettings SaveTo(out IGPALGrid<string> dataGrid)
684 {
685 DatabaseHelper.GetGrid(DatabaseSettings, out dataGrid);
686 return this;
687 }
688
689 public IAllowDatabaseSettings GetRowCount(out List<int> rowCountsPerTable)
690 {
691 rowCountsPerTable = DatabaseSettings.LastGridRowCountsPerTable ?? new List<int>();
692 return this;
693 }
694
695 public IAllowDatabaseSettings SaveTo(out DataTable dataTable)
696 {
697 DatabaseHelper.GetDataTable(DatabaseSettings, out dataTable);
698 return this;
699 }
700
701 public IAllowDatabaseSettings SaveTo(out DataSet dataSet)
702 {
703 DatabaseHelper.GetDataSet(DatabaseSettings, out dataSet);
704 return this;
705 }
706
721 public IAllowDatabaseSettings SaveTo(out string value)
722 {
723 DatabaseHelper.GetScalar(DatabaseSettings, out value);
724 return this;
725 }
726
727 public IAllowParametersAndActions Execute(out int rowCount)
728 {
729 rowCount = DatabaseHelper.ExecuteSQL(DatabaseSettings, DatabaseSettings.SqlCommand, ParameterTokens());
730 return this;
731 }
732
733 public IAllowParametersAndActions Execute(out List<int> rowCountsPerStatement)
734 {
735 DatabaseHelper.ExecuteSQL(DatabaseSettings, DatabaseSettings.SqlCommand, ParameterTokens(), out rowCountsPerStatement);
736 return this;
737 }
738
746 private IGPALGrid<string> ParameterTokens()
747 {
748 IGPALGrid<string> retVal = DatabaseSettings.ParametersGrid;
749
750 if (null == retVal || 0 == retVal.Rows)
751 {
752 Tokens.Clear();
753 Tokens.AddRow(DatabaseSettings.ParametersList);
754
755 retVal = Tokens;
756 }
757
758 return retVal;
759 }
760
761 public IAllowDatabaseSettings WithErrorCallback(GPALDatabase.CallOnError callOnError)
762 {
763 DatabaseSettings.CallOnErrorHandler = callOnError;
764 return this;
765 }
766 #endregion <Output Column Settings [saving not yet implemented]>
767 #endregion <Fluent Methods>
768 #region <Private>
769 #endregion <Private>
770 }
771}
772
What went wrong on the last call, and enough about it to act on. A failure hands the callback the da...
string Message
What the failure said.
string Sql
The sql that was running.
Exception Exception
The exception itself, for a caller that wants the stack.
int Number
The server's error number, 0 when the failure did not come from the server.
int Row
Which row of Parameters was being written, -1 when the failure was not on a row.
IGPALGrid< string > Parameters
The rows of parameters the sql was running against.
List< string > RowValues
That row's values, so the callback does not have to index back into the grid.
Class to define database usage. Currently only used for input from a table, sql or stored procedure....
IAllowDatabaseSettings WithUsername(string username)
Set the username used to connect to the database. If no connection string is provided,...
IAllowDatabaseSettings WithStoredProcedure(string storedProcedure)
The stored procedure to execute for the input data grid (tokens).
IAllowDatabaseSettings Close()
Closes the connection and rolls back a transaction still open on it. The database can be used again a...
IGPALGrid< string > Tokens
The rows/columns returned from the database. Tokens are loaded upon calling any [Append/FillIn/Inser...
DatabaseError LastError
The last failure, or null when the last call did not fail. Cleared at the start of every call,...
IAllowDatabaseSettings WithCommandTimeout(int timeoutInSeconds)
Set the numbers of rows to select from the table.
IAllowDatabaseSettings WithTableName(string tableName)
Set the table name to query for input. If no SQL command or stored procedure is defined,...
IAllowDatabaseSettings WithConnectionString(string connectionString)
Set the connectrion string used to connect to the database. If no connection string is provided,...
IAllowDatabaseSettings SaveTo(out string value)
Reads a single value: the first column of the first row, without building a grid around it....
IAllowDatabaseSettings WithDatabaseType(Enums.DatabaseType databaseType)
Set the type of database GPAL is interacting with. Currently only SQL Server is supported....
IAllowParametersAndActions Execute(out List< int > rowCountsPerStatement)
Per-statement breakdown (via SqlCommand.StatementCompleted) instead of the single aggregate count.
IAllowParametersAndActions WithParameterGrid(IGPALGrid< string > parametersGrid)
Add a parameter to the parameters list. .
IAllowDatabaseSettings WithPassword(string password)
Set the password used to connect to the database. If no connection string is provided,...
IAllowDatabaseSettings WithServerName(string serverName)
Set the database servername. Used with databasename and [optional] usename/password to construct a c...
IAllowDatabaseSettings WithSQLCommand(string sqlCommand)
The SQL command to execute for the input data grid (tokens).
IAllowDatabaseSettings WithDatabaseName(string databaseName)
Set the database instance name. Used with database servername and [optional] usename/password to con...
void Dispose()
Closes the connection, so a database can be held in a using block. Same work as Close.
IAllowParametersAndActions WithParameter(string parameter)
Add a parameter to the parameters list. .
IAllowParameters ClearParameters
If you have already defined parameter objectss on a GPAL.Database, you can clear them and add new par...
IAllowDatabaseSettings GetRowCount(out List< int > rowCountsPerTable)
Only SaveTo(out IGPALGrid<string>) returns this - meaningless for SaveTo(DataTable)/SaveTo(DataSet),...
IAllowDatabaseSettings WithParameterName(string parameterName)
Add a parameter name to the parameter names list. CAVEAT: we need to know the type of parameter,...
SaveTo(...) already ran the read (an out parameter forces that), so chaining .Execute() afterward wou...