C# 利用数据集/数据适配器的intel手动更新数据存储
我有一个数据集,其中有两个表按照关系绑定在一起:父表和子表 我不能使用DataAdapter来更新数据库,而是必须调用存储过程来根据数据行的状态更新数据库 不用说在父对象的删除(没有级联删除的实现)上,必须先删除子对象。相反,要创建一个孩子,我需要确保父母存在,如果不存在,就创建它 例如,如果数据集有新添加的父记录和子记录,则必须首先创建父记录,并且在创建子记录时需要考虑新创建的父行的主键 当涉及DataAdapter时,所有这些问题都会以静默方式解决。然而,由于我被迫处理CUD(在CRUD中),我必须将其构建到我的代码中,在数据集中搜索关系,检查父记录和子记录的行状态,在尝试插入之前确保父记录的子外键是正确的,等等 是否可以利用boiler plate.NET(ADO)来简化此过程?也许是通过检查DataAdapter执行工作时会发生的执行计划 ------------------------------------编辑---------------------- 我不认为这会有帮助,但这里有一些我为处理表而编写的相当简单的代码。根据代码,我没有考虑到新的父级和子级在数据集中都会有帮助,因为迄今为止的所有测试都假设父级已经存在。这个假设之所以成立,是因为,奇怪的是,我最初被要求在UAT环境中进行开发——go图 无论如何,我会检查对特定表的更改,如果找到,我会进行处理。同样,没有考虑到父母和孩子都有新的记录。下面的代码通过先检查子表,然后检查父表来处理删除,但除此之外,两个表上可能都有NEW、UPDATE和DELETE,因此复杂性(至少现在)是压倒性的 对表的更改进行高级检查。我添加了一个优先级变量,以便以后进行排序。根据这个例子,我将两个优先级值都设置为“1”,这意味着一种关系,我将在以后进行资本化C# 利用数据集/数据适配器的intel手动更新数据存储,c#,ado.net,dataset,crud,dataadapter,C#,Ado.net,Dataset,Crud,Dataadapter,我有一个数据集,其中有两个表按照关系绑定在一起:父表和子表 我不能使用DataAdapter来更新数据库,而是必须调用存储过程来根据数据行的状态更新数据库 不用说在父对象的删除(没有级联删除的实现)上,必须先删除子对象。相反,要创建一个孩子,我需要确保父母存在,如果不存在,就创建它 例如,如果数据集有新添加的父记录和子记录,则必须首先创建父记录,并且在创建子记录时需要考虑新创建的父行的主键 当涉及DataAdapter时,所有这些问题都会以静默方式解决。然而,由于我被迫处理CUD(在CRUD中)
var ds = myDataSet; //easier to reference!
#region Delegates
Func<DBTable, bool> ChangesFound = (table) =>
{
var tbl = table.ToString();
if (ds.Tables.Contains(tbl) && ds.Tables[tbl].HasChanges())
return true;
else
return false;
};
Func<DBTable, DataTable> Table = (table) =>
{
var tbl = table.ToString();
return ds.Tables[tbl];
};
#endregion Delegates
if (ChangesFound(DBTable.CHILD)) Process_CHILD(1,Table(DBTable.CHILD));
if (ChangesFound(DBTable.PARENT)) Process_PARENT(1,Table(DBTable.PARENT));
父调用根据执行的活动设置第二级优先级。作为父级,插入优先于删除,因此具有最高优先级
public void Process_CHILD(int priority, DataTable modifiedTable)
{
foreach (DataRow row in modifiedTable.Rows)
{
if (row.RowState != DataRowState.Added && row.RowState != DataRowState.Modified && row.RowState != DataRowState.Deleted)
continue;
DataRowVersion rowVersion = row.RowState == DataRowState.Deleted ? DataRowVersion.Original : DataRowVersion.Current;
if (row.RowState == DataRowState.Added)
{
PriorityTwo = 1;
// Build Insert Stored Procedure
}
else if (row.RowState == DataRowState.Modified)
{
PriorityTwo = 2;
// Build Update Stored Procedure
}
else if (row.RowState == DataRowState.Deleted)
{
PriorityTwo = 3;
// Build Delete Stored Procedure
}
}
}
public void Process_PARENT(int priority, DataTable modifiedTable)
{
foreach (DataRow row in modifiedTable.Rows)
{
if (row.RowState != DataRowState.Added && row.RowState != DataRowState.Modified && row.RowState != DataRowState.Deleted)
continue;
DataRowVersion rowVersion = row.RowState == DataRowState.Deleted ? DataRowVersion.Original : DataRowVersion.Current;
if (row.RowState == DataRowState.Added)
{
PriorityTwo = 3;
// Build Insert Stored Procedure
}
else if (row.RowState == DataRowState.Modified)
{
PriorityTwo = 2;
// Build Update Stored Procedure
}
else if (row.RowState == DataRowState.Deleted)
{
PriorityTwo = 1;
// Build Delete Stored Procedure
}
}
}
创建所有存储过程后,我将按优先级变量进行排序,并对每个变量进行处理。同样,因为我已将这两个表的第一级优先级设置为相同,所以它们的所有表处理都同时进行,并按优先级排序
foreach (var proc in storedProcedures
.OrderByDescending(o => o.Priority)
.ThenByDescending(t => t.PriorityTwo))
{
// Call the procedure, etc...
}
这个解决方案非常糟糕:我仍然粗暴地强制执行操作顺序,而实际上我应该依赖于已经定义的数据集关系。我可能最终会构建一些由数据集中定义的关系驱动的东西,但我还是希望在我咬紧牙关重建我认为已经编写好的锅炉板代码之前,已经存在一些东西
如果我的麻烦还不够严重,还有一个问题我需要解决,我在上面提到过。创建父级时,其主键的标识值被传递回c#,我使用它来更新生成存储过程的数据行。我仍然需要弄清楚如何将其传递给子记录,以便在其存储过程插入时保持引用完整性。。。我需要这样做,而不是硬编码
是的,“按现状”的解决方案很糟糕…事实证明,我让这比需要的困难得多。我认为这是因为我厌恶处理数据表关系。但至少对我来说,处理好这份关系清单才是正确的选择 我确定我的解决方案是在任何删除之前执行所有插入,或者在任何插入之前执行所有删除,并且确保在执行任何活动(插入/删除)时,按照数据集中关系规定的正确顺序执行 第一步是生成一个列表,其中列出了DataTable关系中定义为“父”的所有表,这些表在任何其他关系中都从未定义为“子”。这些“父级”稍后将作为层次结构处理顺序中的起点:
var topParents = new List<DataTable>();
foreach (DataRelation rel in ds.Relations)
{
var parent = rel.ParentTable;
var isTopParent = true;
foreach (DataRelation rel2 in ds.Relations)
{
if (parent.TableName == rel2.ChildTable.TableName) isTopParent = false;
}
if (isTopParent && !topParents.Contains(parent))
topParents.Add(parent);
}
然后是删除:
foreach (var table in insertTableOrder.OrderByDescending(o => o.Key))
{
var targetTable = table.Value.GetChanges(DataRowState.Deleted);
if (targetTable != null && targetTable.HasChanges())
{
var methodName = "CreateSP_" + targetTable.TableName;
SetAction(string.Format("Invoking Reflected method: [{0}], for {1}", methodName, purpose), "");
System.Reflection.MethodInfo createSP = this.GetType().GetMethod(methodName);
object result = createSP.Invoke(this, new object[] { index++, targetTable });
}
}
最后,我将处理关系中未定义的所有其他表:
foreach (DataTable table in ds.Tables)
{
if (insertTableOrder.Values.Contains(table)) continue;
if (table.HasChanges())
{
var methodName = "CreateSP_" + table.TableName;
System.Reflection.MethodInfo createSP = this.GetType().GetMethod(methodName);
object result = createSP.Invoke(this, new object[] { index++, table });
}
}
生成所有数据库操作后,是时候执行命令了。插入操作特别需要注意的是数据库(本例中为MSSQL)分配的主键,该主键随后是子级外键约束所要求的。基本“storedProcedures”对象已编码到其中,下面使用了所有必要的元素,以确保在命中数据库之前,父对象的SQLCommand(存储过程)子对象已使用外键正确更新。此外,由于DataAdapter没有使用原始数据集,因此我需要确保数据集反映数据库的状态。因此,对于插入,我更新数据集的每个DataTable中的行,在存储过程执行将其插入后端表时,将主键分配给物理行
下面的代码被包装到一个Try/Catch中,特别是在出现故障时拒绝数据集上的更改:
using (var conn = new System.Data.SqlClient.SqlConnection(ApplicationConfig.DbConnectInfo.ConnectionString))
{
conn.Open();
using (var transaction = conn.BeginTransaction(IsolationLevel.ReadCommitted))
{
foreach (var parent in storedProcedures.OrderBy(o => o.Sequence))
{
var spName = parent.StoredProcedureName;
var originalValue = parent.OriginalPrimaryKeyValue;
var primaryKey = parent.PrimaryKeyUsed;
parent.Execute(conn, transaction);
if (parent.Operation == DatabaseOperaion.Insert)
{
var scopeIdentityValue = (int)parent.SQLParameters[0].Value;
foreach (DataRelation rel1 in ds.Relations)
{
var parentColumn = rel1.ParentColumns[0].ToString().ToLower();
var childColumn = rel1.ChildColumns[0].ToString().ToLower();
var childTable = rel1.ChildTable.TableName.ToLower();
//if (parent.Table == rel1.ParentTable)
if (parent.Table.TableName == rel1.ParentTable.TableName)
{
var children = storedProcedures
.Where(w => w.Table.TableName.ToLower() == childTable);
foreach (var child in children)
foreach (var parm in child.SQLParameters)
if (parm.ParameterName.Substring(1).ToLower() == childColumn)
if ((decimal)parm.Value == originalValue)
parm.Value = scopeIdentityValue;
}
var where = string.Format("{0}={1}", primaryKey, originalValue);
DataRow[] rows = ds.Tables[parent.Table.TableName].Select(where);
foreach (DataRow row in rows)
{
row.BeginEdit();
row[primaryKey] = scopeIdentityValue;
row.EndEdit();
}
}
action = string.Empty;
}
}
transaction.Commit();
ds.AcceptChanges();
} // Transaction
} // SQL Connection
无论如何,不用说,上面的解决方案是针对我的需要的,可能不符合您的特定目的。但我真的希望它能帮助你克服遇到的任何困难。事实证明,我让这件事变得比实际需要困难得多。我想这是因为我讨厌使用DataTabl
foreach (var table in insertTableOrder.OrderByDescending(o => o.Key))
{
var targetTable = table.Value.GetChanges(DataRowState.Deleted);
if (targetTable != null && targetTable.HasChanges())
{
var methodName = "CreateSP_" + targetTable.TableName;
SetAction(string.Format("Invoking Reflected method: [{0}], for {1}", methodName, purpose), "");
System.Reflection.MethodInfo createSP = this.GetType().GetMethod(methodName);
object result = createSP.Invoke(this, new object[] { index++, targetTable });
}
}
foreach (DataTable table in ds.Tables)
{
if (insertTableOrder.Values.Contains(table)) continue;
if (table.HasChanges())
{
var methodName = "CreateSP_" + table.TableName;
System.Reflection.MethodInfo createSP = this.GetType().GetMethod(methodName);
object result = createSP.Invoke(this, new object[] { index++, table });
}
}
using (var conn = new System.Data.SqlClient.SqlConnection(ApplicationConfig.DbConnectInfo.ConnectionString))
{
conn.Open();
using (var transaction = conn.BeginTransaction(IsolationLevel.ReadCommitted))
{
foreach (var parent in storedProcedures.OrderBy(o => o.Sequence))
{
var spName = parent.StoredProcedureName;
var originalValue = parent.OriginalPrimaryKeyValue;
var primaryKey = parent.PrimaryKeyUsed;
parent.Execute(conn, transaction);
if (parent.Operation == DatabaseOperaion.Insert)
{
var scopeIdentityValue = (int)parent.SQLParameters[0].Value;
foreach (DataRelation rel1 in ds.Relations)
{
var parentColumn = rel1.ParentColumns[0].ToString().ToLower();
var childColumn = rel1.ChildColumns[0].ToString().ToLower();
var childTable = rel1.ChildTable.TableName.ToLower();
//if (parent.Table == rel1.ParentTable)
if (parent.Table.TableName == rel1.ParentTable.TableName)
{
var children = storedProcedures
.Where(w => w.Table.TableName.ToLower() == childTable);
foreach (var child in children)
foreach (var parm in child.SQLParameters)
if (parm.ParameterName.Substring(1).ToLower() == childColumn)
if ((decimal)parm.Value == originalValue)
parm.Value = scopeIdentityValue;
}
var where = string.Format("{0}={1}", primaryKey, originalValue);
DataRow[] rows = ds.Tables[parent.Table.TableName].Select(where);
foreach (DataRow row in rows)
{
row.BeginEdit();
row[primaryKey] = scopeIdentityValue;
row.EndEdit();
}
}
action = string.Empty;
}
}
transaction.Commit();
ds.AcceptChanges();
} // Transaction
} // SQL Connection