传递xml字符串作为MySQL存储过程的输入

时间:2017-03-30 07:34:15

标签: c# mysql sql-server xml stored-procedures

我们使用以下代码示例通过存储过程(SQL Server)将数据从我的应用程序发送到外部数据库。

这里我也需要支持MySQL。因此,基于最终用户的数据库选择,我们需要将数据发送到MySQL或SQL Server

c#代码将在另一台机器上运行,而DB服务器将是不同的服务器。

C#代码

using (SqlConnection sqlConnection = new SqlConnection(<<MyConnectionString>>))
{
 using (SqlCommand sqlCommand = new SqlCommand(<<StoredProcedureName>>, sqlConnection))
 {
   sqlCommand.CommandType = CommandType.StoredProcedure;
   sqlCommand.Parameters.Add("tblStudent", SqlDbType.Xml).Value = students.ToList().ToXML();
   sqlConnection.Open();
   sqlCommand.ExecuteNonQuery();
 }
}

存储过程

CREATE PROCEDURE `usp_UpdateStudent` (@tblStudent  XML)
BEGIN
  SET NOCOUNT ON;
  INSERT INTO Student(StudentId,StudentName)
  SELECT    Student.value('(StudentId)[1]', 'nvarchar(100)') as StudentId,
    Student.value('(StudentName)[1]',  'nvarchar(100)') as StudentName
  FROM
  @tblStudent.nodes('/ArrayOfStudent/Student')AS TEMPTABLE(Student)
END

我在网上搜索了如何将xml字符串作为输入参数从c#传递给存储过程。但我没有得到任何具体的答案。

请提供有关如何使用XML作为输入参数创建存储过程的建议,以及如何将XML字符串从c#传递到该文件。

注意:上述代码在SQL Server中按预期工作。当我尝试用MySQL实现相同的功能时,我发现MySQL在存储过程中不支持xml作为输入类型参数。看起来我需要将xml作为普通文本传递并解析存储过程中的文本。

如果有更有效的方法,请告诉我。

2 个答案:

答案 0 :(得分:0)

使用Load_File()将xml数据导入局部变量,然后ExtractValue()使用XPath查询XML数据。例如,在下面的代码中,它从xml_content变量中检索学生数:

declare xml_content text;
 declare v_row_count int unsigned;  
 set xml_content = load_file(path);
 set v_row_count = extractValue(xml_content, concat('count(', node, ')'))

路径:'C:\ students1.xml',节点:'/ student_list / student'

答案 1 :(得分:0)

我意识到这是一个老问题,但我也没能找到一个好的答案,所以我被迫自己做了一些工作!最终结果似乎有效。根据您的需要进行调整,可以为您提供以下内容:

DELIMITER $$

CREATE procedure `usp_InsertStudent` (ptblStudent  text)
BEGIN 

declare cnt int;
declare ptr int;
declare rowPtr varchar(100);

set cnt = (extractValue(ptblStudent, 'count(/ArrayOfStudent/Student)'));
set ptr = 0;

while ptr < cnt do
    SET ptr = ptr + 1;
    SET rowPtr = concat('/ArrayOfStudent/Student[', ptr, ']');
    INSERT INTO Student (StudentId,StudentName)
    values (extractValue(ptblStudent, concat(rowPtr, '/StudentId')),
            extractValue(ptblStudent, concat(rowPtr, '/StudentName')));

end while;  

SELECT ptr;

END;

$$ 
DELIMITER ;

顺便说一句 - 我改变了例程名称(你的例子是插入而不是更新)。

作为解释。如果执行extractValue('/ArrayOfStudent/Student/StudentId'),则会得到一个结果字符串,其中所有值都以空格分隔。鉴于在MySQL中分割字符串并不是那么容易(在我有限的经验中),逐行提取单个值似乎更好(当有很多字段时尤其如此 - 我首先尝试提取所有字段一旦进入空格分隔的字符串,然后将字符串拆分为单独的临时表,每个临时表都有一个auto_increment id,然后加入id上的临时表,但是当需要三个以上的字段时,这很快变得混乱),因此需要检查行计数,以及while循环的使用。这确实意味着插入成为单个插入,这就是我返回ptr以指示添加的行数而不是row_count()(在这种情况下为1!)的原因。

在隐式转换时,我对MySQL的灵活性感到非常高兴:除了字符串之外,我到目前为止还成功测试了整数,双打和DateTimes - 迄今为止还没有必要进行明确的转换。

在更一般的层面上,您的问题也引发了向多个数据提供商编码的问题。几年前,我的一位同事说服我按照部分评论中提出的DbConnection路线行事。事后看来,这是一个错误。问题是,您失去了利用通过其.Net库公开的一个或其他数据库提供程序的特定功能的机会。所以我现在所做的就是你提出的建议:我通过接口定义我的数据访问层;然后,每个数据库提供程序由一个类实现此接口,每个类使用本机.Net库。因此SQL Server实现使用SqlConnection,MySQL实现MySqlConnection,Oracle实现OracleConnection等。通过这种方式,我的应用程序不关心实现细节,但我可以自由地利用一个或另一个db独有的功能。举一个简单的例子:MS SQL服务器允许存储过程返回多个记录集(具有不同的字段),从而允许您在一次调用db中填充复杂的DataSet;我使用的所有其他dbs,每个记录集需要一个过程,因此必须在DAL中构建DataSet。对于应用程序,没有区别,因为接口需要返回DataSet。