ASP.NET 中使用 XQuery 和反射进行批量 CRUD 操作






4.72/5 (21投票s)
使用 Xquery、反射在 ASP.NET 中实现批量 CRUD 操作的简单方法,附带示例
引言
在数据库术语中,CRUD 代表四种基本数据库操作:创建
(Create)、读取
(Read)、更新
(Update)和删除
(Delete)。无论应用程序如何多样,大多数现实世界的应用程序都是以数据为中心,更确切地说,是以 CRUD 为中心。在某个时候,用户的大量数据需要写入数据库。通常,这通过重复的 DML 数据库调用来完成。更大数量的数据需要更昂贵的数据库调用(在内存和进程方面),并且会带来更大的数据不一致风险。如果我们能够以最少的数据库调用来完成操作,我们就可以实现更快的处理速度、更一致的数据以及更好的用户体验。考虑到这一点,在我一个大型数据驱动的项目中,我使用了 xml/xquery 进行 CRUD 操作,并希望通过一个简单的例子分享。
使用 XML 进行 CRUD?
XML 在现代信息系统、工业、政府和学术界得到广泛应用。许多商业数据库管理系统支持 XML 存储。然而,由于它们类型系统的表达能力差异以及将对象查询翻译成 XQuery 等 XML 查询语言的难度,自动进行 XML 和对象之间的翻译问题在很大程度上仍未解决。在混合关系/XML 数据库中,由于 XML 数据可以分布在多个关系表中,对象-关系阻抗不匹配问题使得这个问题更加复杂。SQL Server 2005 中引入了备受赞誉的 XML 数据类型。这种新的数据类型允许创建专门用于存储 XML 数据的变量和列,无论是整个 XML 文档还是其中的一部分内容。
使用 XQuery 进行 CRUD
XML 最新的发展之一是出现了原生的 XML 查询和转换语言 XQuery。XQuery 通常是完美的语言,它提供了一种简单优雅的方式来计算 XML 文档内容的值。使用 XML 而不是典型的 CRUD 操作方法的几个原因。这些原因包括:
- 最佳性能,TSQL 使用 XQuery 解析 XML 速度极快
- 将 CRUD 逻辑与应用程序的层分离
- 防止 SQL 注入攻击
- 节省应用程序到数据库的往返时间
SQL Server 在基于集合的操作方面表现最佳。逐行更新比一次更新多行要慢。在我们的示例中,假设营销人员想更改客户 ID 为 1、3、5 的电子邮件 ID,他们会选择数据网格中的项目,指定新的电子邮件 ID,然后单击“保存”按钮。您如何将 ID 列表作为表传递给 SQL Server?最简单的方法是将 ID 作为 XML 数据类型实例传递,然后使用nodes()
和value()
方法将 XML 实例转换为表,并将该表与Product
表连接。这样,更新就是基于集合的。
示例问题场景
假设我们要为营销系统构建一个仪表板,营销人员可以不断输入潜在的新客户信息并更新现有客户信息。需求是,通过仪表板,营销人员可以一次输入多个客户信息并保存所有数据,而不是让营销人员更新/插入单个记录。您可以看到,下面是应用程序仪表板 UI,营销人员可以通过点击“添加更多”按钮来添加多个客户。批量 CRUD 操作通过“全部保存”/“全部更新”/“全部删除”按钮来完成。
问题的解决方案
上述场景的功能可以通过使用 xml/xquery 轻松实现。我们可以将实现步骤总结如下:
- 我们需要一个自定义类,其中包含在数据库中进行
insert
/update
/delete
操作所需的所有客户属性。 - 根据每个客户填充该类,并在运行时形成一个集合。
- 使用反射将该集合转换为内存中的 XML 格式。
- 将此格式化的 XML 作为参数值传递给数据库存储过程以进行 CRUD 操作。
- 使用 xquery,我们将解析参数值并根据需要应用 DML。
使用此方法的优点是:
- 我们可以使用最少的参数将数据传递给数据库(更准确地说,只需要传递一个 XML 类型的参数)。
- 我们可以一次性传递大量数据进行 CRUD 操作。
- 随着数据量的增长,这不会影响应用程序的数据库调用。
- 维护数据完整性很简单。
- 由于数据结构灵活,未来在 DAL(数据访问层)中进行更改很容易。
实现
现在我将按照上述步骤实现解决方案。对于这个特定的实现,我将使用上面提到的问题场景并相应地实现。在获得想法后,您可以根据需要自定义以实现您版本的解决方案。我们将创建一个实体类来保存客户属性,并将其命名为Customer
。该类将在 UI 的每次update
/save
事件后填充,并添加到customerList
集合中以进行批量操作。我们将使用反射将此集合转换为 XML。
foreach (Customer xml in customerList)
{
XmlElement element = doc.CreateElement("data");
PropertyInfo[] allProperties = xml.GetType().GetProperties();
foreach (PropertyInfo thisProperty in allProperties)
{
object value = thisProperty.GetValue(xml, null);
XmlElement tmp = doc.CreateElement(thisProperty.Name);
if (value != null)
{
tmp.InnerXml = value.ToString();
}
else
{
tmp.InnerXml = string.Empty;
}
element.AppendChild(tmp);
}
node.AppendChild(element);
}
转换后,生成的 XML 将如下所示:
生成 XML 后,会将此 XML 作为参数传递给 SQL Server 存储过程。
using (SqlConnection cn = new SqlConnection(GetConnectionString()))
{
SqlCommand sqlCmd = new SqlCommand();
sqlCmd.Connection = cn;
sqlCmd.Connection.Open();
sqlCmd.CommandType = CommandType.StoredProcedure;
sqlCmd.CommandText = "spr_Sync_Customer";
sqlCmd.Parameters.Add("@XMLDATA", SqlDbType.Xml, Int32.MaxValue);
sqlCmd.Parameters["@XMLDATA"].Direction = ParameterDirection.Input;
sqlCmd.Parameters["@XMLDATA"].Value = xml;
sqlCmd.ExecuteScalar();
sqlCmd.Connection.Close();
sqlCmd.Connection.Dispose();
}
在这种特定情况下,spr_Sync_Customer
是存储过程。在存储过程中,XML 数据使用 Xquery 进行解析,并执行相应的insert
/update
/delete
操作。
在这种特定情况下,@XMLDATA
是为提供的 XML 准备的存储过程输入参数。解析 XML,您可以根据需要使用解析后的数据填充数据库表,在此示例中,我填充了Customer
表。
结论
上面给出的示例非常基础,旨在为使用 Xquery 进行 CRUD 提供一个基本的入门。您可以使用该概念来处理更复杂的想法,为了获得更好的性能,请尽可能在 XML 数据类型上使用exist()
方法。如果您想了解更多关于 Xquery 的知识,可以在这里找到非常有用的指南。示例源代码包含在本文中,您可以根据需要自由修改。
历史
- 版本 1.0 @2010 年 11 月 30 日