Hallo Leute,
ich habe ein DataGridView Control, welches bearbeitet werden kann,
die änderungen im Grid werden auf das darunter liegende DataTable Objekt übertragen.
Per SQLiteDataAdapter führe ich dann die änderungen auf die Datenbank aus.
Problem ist, dass das DataAdapter Objekt jegliche Contraints außer kraft setzt...
Beispiel mit 2 Tabellen:
Spoiler anzeigen
Table tbl_CostCenter:
- CostCenterPK ( VARCHAR(12) UNIQUE NOT NULL PRIMARY KEY)
- Description (TEXT)
Table tbl_Employee
- [Standard Felder wie Name Abteilung etc...]
- CostCenterFK (FOREIGN KEY NOT NULL)
Versuche ich in der Datenbank direkt eine Kostenstelle zu löschen, erhalte ich einen Fehler wegen des FOREIGN KEY NOT NULL Constraints.
Das ist klasse.
Lösche ich per SQLiteDataAdapter wird dies komplett ignoriert und der Datensatz wird einfach aus der Tabelle tbl_CostCenter gelöscht.
Hier zusätzlich noch ein bisschen Code welcher ausgeführt wird um die Datenbank zu updaten:
Spoiler anzeigen
Button Click events die die Änderungen gültig machen:
Spoiler anzeigen
boldUpdate Methode:
Spoiler anzeigen
EDIT 2019-02.08:
Spoiler anzeigen
Ich gehe vom DataAdapter weg und mache es etwas "aufwändiger", aber dafür funktioniert es wenigstens.
Per DataTable Extension Class hole ich mir jetzt alle modifizierten DataRows des DataTable.
Spoiler anzeigen
Extension:
Ausschnitt aus dem Button code:
Danach teile ich nach Art der Modifikation auf und entscheide somit welche SQL Queries generiert werden müssen.
SELECT, UPDATE, DELETE, INSERT werden jeweils in eigenen Transactions ausgeführt.
Waren die DELETE Statements also inkorrekt oder "verboten" werden ansonsten alle anderen normal durchgeführt.
Edit 2019-02-18:
Hallo Leute,
ich habe das oben beschriebene Problem gelöst, indem ich den DataAdapter mehr oder weniger selbst gemacht habe.
Ich verwende 2 DataTable Extensions um die geänderten Rows in einem Struct zu erhalten.
Per class, welche den jeweiligen Table darstellt, erstelle ich mir meine Queries genauso wie ich sie brauche.
! Ab hier nur noch Code !
DataTable Extensions:
Spoiler anzeigen
EditDataRow Struct:
Beispiel Table class welche Queries generiert:
Spoiler anzeigen
Exceptions:
Spoiler anzeigen
Button Click Event Code:
Spoiler anzeigen
ich habe ein DataGridView Control, welches bearbeitet werden kann,
die änderungen im Grid werden auf das darunter liegende DataTable Objekt übertragen.
Per SQLiteDataAdapter führe ich dann die änderungen auf die Datenbank aus.
Problem ist, dass das DataAdapter Objekt jegliche Contraints außer kraft setzt...
Beispiel mit 2 Tabellen:
Table tbl_CostCenter:
- CostCenterPK ( VARCHAR(12) UNIQUE NOT NULL PRIMARY KEY)
- Description (TEXT)
Table tbl_Employee
- [Standard Felder wie Name Abteilung etc...]
- CostCenterFK (FOREIGN KEY NOT NULL)
Versuche ich in der Datenbank direkt eine Kostenstelle zu löschen, erhalte ich einen Fehler wegen des FOREIGN KEY NOT NULL Constraints.
Das ist klasse.
Lösche ich per SQLiteDataAdapter wird dies komplett ignoriert und der Datensatz wird einfach aus der Tabelle tbl_CostCenter gelöscht.
Hier zusätzlich noch ein bisschen Code welcher ausgeführt wird um die Datenbank zu updaten:
Button Click events die die Änderungen gültig machen:
C#-Quellcode
- // Deletes the current Row from the DataGridView DataSource
- // IMPORTANT NOTE: Do not use AcceptChanges() on DataTable yet.
- // The SQLDataAdapter will not recognize the changes made
- // due to the RowState Property of DataTable
- private void btn_Remove_Click(object sender, EventArgs e)
- {
- _lastPressed = LastPressed.DELETE;
- moveActiveOperationPanel(sender);
- var row = ((DataRowView)buffered_TableView.CurrentRow.DataBoundItem).Row;
- var source = (DataTable)buffered_TableView.DataSource;
- row.Delete();
- }
- // Update the Database and reload the DataTable at the end
- private void btn_Commit_Click(object sender, EventArgs e)
- {
- _lastPressed = LastPressed.COMMIT;
- moveActiveOperationPanel(sender);
- var table = (DataTable)buffered_TableView.DataSource;
- _tcn.boldUpdate(ref table);
- displayData(_currentTable);
- }
boldUpdate Methode:
C#-Quellcode
- public bool boldUpdate(ref System.Data.DataTable table)
- {
- if (!connected())
- throw new SQLiteException(SQLiteErrorCode.CantOpen, "Die Datenbank konnte nicht geöffnet werden");
- string select = $"SELECT * FROM {table.TableName};";
- SQLiteDataAdapter sqlDa = null;
- SQLiteCommandBuilder builder = null;
- try
- {
- sqlDa = new SQLiteDataAdapter();
- sqlDa.SelectCommand = new SQLiteCommand(select, _db.Connection);
- builder = new SQLiteCommandBuilder(sqlDa);
- sqlDa.UpdateCommand = builder.GetUpdateCommand();
- sqlDa.ContinueUpdateOnError = false;
- sqlDa.Update(table);
- return true;
- } catch (Exception ex) {
- var a = ex.Message;
- return false;
- } finally {
- try {
- if (!global_db_state_open)
- _db.Connection.Close();
- if (builder != null)
- builder.Dispose();
- if (sqlDa != null)
- sqlDa.Dispose();
- } catch { }
- }
- }
EDIT 2019-02.08:
Ich gehe vom DataAdapter weg und mache es etwas "aufwändiger", aber dafür funktioniert es wenigstens.
Per DataTable Extension Class hole ich mir jetzt alle modifizierten DataRows des DataTable.
Extension:
C#-Quellcode
- public static class DataTableExtension
- {
- public static int getIndex(this DataRow r)
- {
- DataTable parent = r.Table;
- if (parent == null)
- return -1;
- int i = 0;
- foreach (DataRow row in parent.Rows)
- {
- if (row == r)
- return i;
- i++;
- }
- return -1;
- }
- public static List<DataRow> modifiedRows(this DataTable table, DataRowState state)
- {
- if (table.Rows.Count==0)
- return new List<DataRow>();
- List<DataRow> modified = new List<DataRow>();
- foreach (DataRow row in table.Rows) {
- if ( (row.RowState & state) == DataRowState.Added
- || (row.RowState & state) == DataRowState.Deleted
- || (row.RowState & state) == DataRowState.Modified
- || (row.RowState & state) == DataRowState.Detached
- || (row.RowState & state) == DataRowState.Unchanged
- ) modified.Add(row);
- }
- return modified;
- }
- }
Ausschnitt aus dem Button code:
C#-Quellcode
- // Get DataTable from GridView Datasource
- var table = (DataTable)buffered_TableView.DataSource;
- // Get all modified DataRows by calling the modifiedRows() extension Method
- var modified = table.modifiedRows(
- DataRowState.Modified
- | DataRowState.Deleted
- | DataRowState.Added
- );
- // Get the indizes of the modified rows
- var indizes = modified.Select( m => m.getIndex()).ToList();
Danach teile ich nach Art der Modifikation auf und entscheide somit welche SQL Queries generiert werden müssen.
SELECT, UPDATE, DELETE, INSERT werden jeweils in eigenen Transactions ausgeführt.
Waren die DELETE Statements also inkorrekt oder "verboten" werden ansonsten alle anderen normal durchgeführt.
Edit 2019-02-18:
Hallo Leute,
ich habe das oben beschriebene Problem gelöst, indem ich den DataAdapter mehr oder weniger selbst gemacht habe.
Ich verwende 2 DataTable Extensions um die geänderten Rows in einem Struct zu erhalten.
Per class, welche den jeweiligen Table darstellt, erstelle ich mir meine Queries genauso wie ich sie brauche.
! Ab hier nur noch Code !
DataTable Extensions:
C#-Quellcode
- public static class DataTableExtension
- {
- public static int getIndex(this DataRow r)
- {
- DataTable parent = r.Table;
- if (parent == null)
- return -1;
- int i = 0;
- foreach (DataRow row in parent.Rows)
- {
- if (row == r)
- return i;
- i++;
- }
- return -1;
- }
- public static List<DataRow> modifiedRows(this DataTable table, DataRowState state)
- {
- if (table.Rows.Count==0)
- return new List<DataRow>();
- List<DataRow> modified = new List<DataRow>();
- foreach (DataRow row in table.Rows) {
- if ( (row.RowState & state) == DataRowState.Added
- || (row.RowState & state) == DataRowState.Deleted
- || (row.RowState & state) == DataRowState.Modified
- || (row.RowState & state) == DataRowState.Detached
- || (row.RowState & state) == DataRowState.Unchanged
- ) modified.Add(row);
- }
- return modified;
- }
- public static List<EditDataRow> copyAndReject(this DataTable table, DataRowState state)
- {
- List<EditDataRow> copies = new List<EditDataRow>();
- List<DataRow> modified = table.modifiedRows(state);
- if (!modified.Any())
- return new List<EditDataRow>();
- for (int i=0; i<modified.Count; i++)
- {
- var row = new EditDataRow();
- var rowState = modified[i].RowState;
- if (rowState == DataRowState.Modified) {
- row.newItemArray = modified[i].ItemArray;
- }
- if (rowState == DataRowState.Deleted || rowState == DataRowState.Unchanged || rowState == DataRowState.Modified) {
- modified[i].RejectChanges();
- modified[i].AcceptChanges();
- }
- row.parent = table;
- row.rowState = rowState;
- row.itemArray = modified[i].ItemArray;
- if (rowState != DataRowState.Added && rowState != DataRowState.Detached)
- row.index = modified[i].getIndex();
- else
- row.index = table.Rows.Count;
- copies.Add(row);
- }
- return copies;
- }
EditDataRow Struct:
Beispiel Table class welche Queries generiert:
C#-Quellcode
- public interface ITableObject
- {
- string buildSelect();
- string buildSelectThis();
- string buildInsert();
- string buildDelete();
- string buildQuery(System.Data.DataRowState state);
- }
- class UserObject : ITableObject
- {
- private int _userPK;
- private string _firstname;
- private string _lastname;
- private string _initials;
- private int _centerFK;
- public UserObject()
- {
- }
- public UserObject(int userPK, string firstname, string lastname, string initials, int centerFK)
- {
- UserPK = userPK;
- Firstname = firstname;
- Lastname = lastname;
- Initials = initials;
- CenterFK = centerFK;
- }
- public string buildQuery(System.Data.DataRowState state)
- {
- if (state == DataRowState.Modified)
- return string.Empty;
- switch (state)
- {
- case System.Data.DataRowState.Added:
- return buildInsert();
- case System.Data.DataRowState.Deleted:
- return buildDelete();
- case System.Data.DataRowState.Modified:
- throw new TableObjectMissingException("A object of Type 'UserObject' is required for updating a table!");
- case System.Data.DataRowState.Unchanged:
- throw new UnsupportedDataRowStateException(state);
- case System.Data.DataRowState.Detached:
- throw new UnsupportedDataRowStateException(state);
- default:
- return buildSelectThis();
- }
- }
- public string buildQuery(System.Data.DataRowState state, UserObject old)
- {
- switch (state)
- {
- case System.Data.DataRowState.Added:
- return buildInsert();
- case System.Data.DataRowState.Deleted:
- return buildDelete();
- case System.Data.DataRowState.Modified:
- return buildUpdate(old);
- case System.Data.DataRowState.Unchanged:
- throw new UnsupportedDataRowStateException(state);
- case System.Data.DataRowState.Detached:
- throw new UnsupportedDataRowStateException(state);
- default:
- return buildSelectThis();
- }
- }
- public string buildSelect()
- {
- return "SELECT User_PK, Firstname, Lastname, Initials, Center_FK FROM tbl_User ORDER BY Lastname, Firstname;";
- }
- public string buildSelectThis()
- {
- return
- "SELECT * " +
- "FROM tbl_User " +
- $"WHERE User_PK = {_userPK} AND " +
- $"Firstname = '{_firstname}' AND " +
- $"Lastname = '{_lastname}' AND " +
- $"Initials = '{_initials}' AND " +
- $"Center_FK = {_centerFK};";
- }
- public string buildUpdate(UserObject old)
- {
- string query =
- $"UPDATE tbl_User "+
- $"SET Firstname = '{_firstname}', "+
- $"Lastname = '{_lastname}', "+
- $"Initials = '{_initials}', "+
- $"Center_FK = {_centerFK} "+
- $"WHERE User_PK = {old.UserPK} AND "+
- $"Firstname = '{old._firstname}' AND "+
- $"Lastname = '{old._lastname}' AND "+
- $"Initials = '{old._initials}' AND "+
- $"Center_FK = {old.CenterFK};";
- return query;
- }
- public string buildInsert()
- {
- string query =
- "INSERT OR IGNORE INTO tbl_User ("+
- "Firstname, "+
- "Lastname, "+
- "Initials, "+
- "Center_FK "+
- ") VALUES ("+
- $"'{_firstname}',"+
- $"'{_lastname}',"+
- $"'{_initials}',"+
- $"{_centerFK});";
- return query;
- }
- public string buildDelete()
- {
- return
- "DELETE FROM tbl_User "+
- $"WHERE User_PK = {_userPK} AND "+
- $"Firstname = '{_firstname}' AND "+
- $"Lastname = '{_lastname}' AND "+
- $"Initials = '{_initials}' AND "+
- $"Center_FK = {_centerFK};";
- }
- public int UserPK
- {
- get
- {
- return _userPK;
- }
- private set
- {
- _userPK = value;
- }
- }
- public string Firstname
- {
- get
- {
- return _firstname;
- }
- set
- {
- if (value.Length > 50)
- throw new CharCountException(50, value.Length);
- _firstname = value;
- }
- }
- public string Lastname
- {
- get
- {
- return _lastname;
- }
- set
- {
- if (value.Length > 100)
- throw new CharCountException(100, value.Length);
- _lastname = value;
- }
- }
- public string Initials
- {
- get
- {
- return _initials;
- }
- set
- {
- if (value.Length > 10)
- throw new CharCountException(10, value.Length);
- _initials = value;
- }
- }
- public int CenterFK
- {
- get
- {
- return _centerFK;
- }
- set
- {
- _centerFK = value;
- }
- }
- }
Exceptions:
C#-Quellcode
- #region CharCountException
- [Serializable]
- public sealed class CharCountException : Exception
- {
- public CharCountException()
- {
- throw new CharCountException(
- "Char count of parameter exceeded its allowed maximum");
- }
- public CharCountException(string message)
- : base (message)
- {
- throw new CharCountException(message);
- }
- public CharCountException(string message, Exception inner)
- : base (message, inner)
- {
- throw new CharCountException(message, inner);
- }
- public CharCountException(int max, int inputLength)
- {
- throw new CharCountException(
- $"Value exceeded the allowed maximum length.{Environment.NewLine}" +
- $"Maximum length: {max}{Environment.NewLine}"+
- $"Passed length: {inputLength}");
- }
- }
- #endregion
- #region TableObjectMissingException
- [Serializable]
- public sealed class TableObjectMissingException : Exception
- {
- public TableObjectMissingException()
- {
- throw new TableObjectMissingException(
- "Missing an object of Type ITableObject");
- }
- public TableObjectMissingException(string message)
- : base(message)
- {
- throw new TableObjectMissingException(message);
- }
- public TableObjectMissingException(string message, Exception inner)
- : base (message, inner)
- {
- throw new TableObjectMissingException(message, inner);
- }
- public TableObjectMissingException(string inputQuery, string missingObject)
- {
- throw new TableObjectMissingException(
- $"The input does not contain the required table object!{Environment.NewLine}" +
- $"Input: {inputQuery}{Environment.NewLine}"+
- $"Required Object: {missingObject}");
- }
- }
- #endregion
- #region UnsupportedDataRowStateException
- [Serializable]
- public sealed class UnsupportedDataRowStateException : Exception
- {
- public UnsupportedDataRowStateException()
- {
- throw new UnsupportedDataRowStateException(
- "This type of DataRowState is not supported by classes of the ITableObject");
- }
- public UnsupportedDataRowStateException(string message)
- : base(message)
- {
- throw new UnsupportedDataRowStateException(message);
- }
- public UnsupportedDataRowStateException(string message, Exception inner)
- : base (message, inner)
- {
- throw new UnsupportedDataRowStateException(message, inner);
- }
- public UnsupportedDataRowStateException(System.Data.DataRowState state)
- {
- throw new UnsupportedDataRowStateException(
- $"The DataRowState {state} is not supported by objects of type ITableObject");
- }
- }
- #endregion
Button Click Event Code:
C#-Quellcode
- // Update the Database and reload the DataTable at the end
- private void btn_Commit_Click(object sender, EventArgs e)
- {
- if (_currentTable == DisplayOption.MNET) {
- MessageBox.Show("Alle verändernden Aktionen sind auf dieser Tabelle gesperrt");
- return;
- }
- _lastPressed = LastPressed.COMMIT;
- moveActiveOperationPanel(sender);
- // Get DataTable from GridView Datasource
- var table = (DataTable)buffered_TableView.DataSource;
- // Get array of EditDataRow by calling the copyAndReject Extension
- // This will return an array of a struct that contains all neccessary DataRow information
- // We can then process the EditDataRows to create Queries for each table
- // Queries will be created by ITableObject.buildQuery()
- // This function needs a RowState enum to determine which query needs to be generated
- var modified = table.copyAndReject(
- DataRowState.Modified
- | DataRowState.Deleted
- | DataRowState.Added
- ).ToArray();
- // generateQueries will check the current Table enum
- // It will then create an string array that contains all queries
- // which the user more or less executed in the DataGridView control
- string[] queries = generateQueries(modified);
- // Use the boldCommit() function of the MySQLite TableConnector
- // This will execute all queries without a transaction chain!
- // Changes will be done until it catches any errors
- //
- // NOTE: boldComit(query, true) => true means that all queries will be logged in a file
- _tcn.boldCommit(queries, true);
- // Reject all changes just to be sure that we do not change anything
- // that we do not want to change
- table.RejectChanges();
- // Display the new data by calling the displayData() function
- // This will load the whole Table after each commit click!
- displayData(_currentTable);
- }
- private string[] generateQueries(EditDataRow[] rows)
- {
- switch (_currentTable)
- {
- case DisplayOption.COST_CENTER:
- return generateCenterQueries(rows);
- case DisplayOption.MNET:
- return generateMnetQueries(rows);
- case DisplayOption.PHONE:
- return generatePhoneQueries(rows);
- case DisplayOption.PHONE_USER_MAP:
- return generatePUMapQueries(rows);
- case DisplayOption.USER:
- return generateUserQueries(rows);
- default:
- return new string[0];
- }
- }
- private string[] generateUserQueries(EditDataRow[] rows)
- {
- List<string> queries = new List<string>();
- for (int i=0; i<rows.Length; i++)
- {
- string query = string.Empty;
- UserObject obj = null;
- if (rows[i].rowState == DataRowState.Modified && rows[i].newItemArray != null)
- {
- var oldUser = new UserObject(
- (int)rows[i].itemArray[0],
- rows[i].itemArray[1].ToString(),
- rows[i].itemArray[2].ToString(),
- rows[i].itemArray[3].ToString(),
- (int)rows[i].itemArray[4]);
- obj = new UserObject(
- (int)rows[i].newItemArray[0],
- rows[i].newItemArray[1].ToString(),
- rows[i].newItemArray[2].ToString(),
- rows[i].newItemArray[3].ToString(),
- (int)rows[i].newItemArray[4]);
- query = obj.buildQuery(DataRowState.Modified, oldUser);
- }
- else
- {
- obj = new UserObject(
- (int)rows[i].itemArray[0],
- rows[i].itemArray[1].ToString(),
- rows[i].itemArray[2].ToString(),
- rows[i].itemArray[3].ToString(),
- (int)rows[i].itemArray[4]);
- query = obj.buildQuery(rows[i].rowState);
- }
- if (!string.IsNullOrEmpty(query))
- queries.Add(query);
- }
- return queries.ToArray();
- }
Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „Petersilie“ ()