System.InvalidCastException: Failed to convert parameter value from a XElement to a String

John Saunders picture John Saunders · Dec 16, 2011 · Viewed 8k times · Source

Note: I was unable to find this exact question in a search. I found a somewhat similar question here on Stack Overflow, which led me to the solution. I'm posting the question and the solution so that the next person with the problem can more easily find the solution. I would have made the question CommunityWiki if that were still possible - I'm not looking for rep from this.


I am trying to use ADO.NET to call a SQL Server 2005 stored procedure which accepts a parameter of the Xml type:

CREATE PROCEDURE dbo.SomeProcedure(
    @ListOfIds Xml)
AS
BEGIN
    DECLARE
            @Ids TABLE(ID Int);
    INSERT INTO @Ids
    SELECT ParamValues.ID.value('.', 'Int')
    FROM @ListOfIds.nodes('/Persons/id') AS ParamValues(ID);

    SELECT p.Id,
           p.FirstName,
           p.LastName
    FROM Persons AS p
         INNER JOIN @Ids AS i ON p.Id = i.ID;
END;

I pass the XML as a LINQ to XML XElement object

var idList = new XElement(
    "Persons",
    from i in selectedPeople
    select new XElement("id", i));

later

SqlCommand cmd = new SqlCommand
                 {
                     Connection = conn,
                     CommandText = "dbo.SomeProcedure",
                     CommandType = CommandType.StoredProcedure
                 };
cmd.Parameters.Add(
    new SqlParameter
    {
        ParameterName = "@ListOfIds",
        SqlDbType = SqlDbType.Xml,
        Value = idList)
    });
using (var reader = cmd.ExecuteReader())
{
    // process each row
}

This fails on the ExecuteReader line with the exception:

System.InvalidCastException: Failed to convert parameter value from a XElement to a String. ---> System.InvalidCastException: Object must implement IConvertible

What's the correct way to pass an XElement to a stored procedure?

Answer

John Saunders picture John Saunders · Dec 16, 2011

The SqlClient code doesn't permit an XElement to be passed directly.

One thing you can do is to use the System.Data.SqlTypes.SqlXml class to pass the XML:

cmd.Parameters.Add(
    new SqlParameter
    {
        ParameterName = "@ListOfIds",
        SqlDbType = SqlDbType.Xml,
        Value = new SqlXml(idList.CreateReader())
    });

Depending on your code, you may need to place the XmlReader returned from the CreateReader code into a using block.