简体   繁体   中英

Linq stored procedure to return XML

I am using Entity Framework code-first approach. I want to call a stored procedure from the DbContext class and to get XML output.

Stored procedure (SQL Server):

CREATE PROCEDURE xml_test    
AS
BEGIN
    DECLARE @xml1 xml

    SET @xml1 = (SELECT * from Product FOR XML RAW) 

    SELECT @xml1 AS my_xml
END

LINQ Entity Framework:

using (DBContext db = new DBContext())
{
    var ProductList = await db.Database.ExecuteSqlCommandAsync("exec xml_test");
}

Here the ProductList list is returning -1.

I want to get the xml output which is returned by the stored procedure.

Note: I have also tried methods like: ExecuteSqlCommand, SqlQuery with no help.

Based on MSDN :

For UPDATE, INSERT, and DELETE statements, the return value is the number of rows affected by the command. When a trigger exists on a table being inserted or updated, the return value includes the number of rows affected by both the insert or update operation and the number of rows affected by the trigger or triggers. For all other types of statements, the return value is -1. If a rollback occurs, the return value is also -1.

So ExecuteSqlCommand returns an int for queries like Insert , Update , Delete and it means the number of row affected by a single non-query. So ExecuteSqlCommand is not suitable for querying.

Also this is a common problem because Entity Framework cannot support Stored Procedure Return values out of the box and it is because EF is an ORM, not a SQL replacement. Check the following link for similar problem in Model First :

Getting data from stored procedure with Entity Framework

And this with ExecuteNonQuery :

ExecuteNonQuery returning -1 when using sql COUNT despite the query string

The solution : for queries you need to use Database.SqlQuery<TElement> method:

var ProductList = db.Database.SqlQuery<string>("exec xml_test").ToList();

I think you can use SQLQuery like this:

using (var dbcontext = new DBContext())
{
    //Reading stored procedure results as List<string>
    var r = dbcontext.Database.SqlQuery<string>("EXEC xml_test").ToList(); //Note: EXEC is optional

    //Joining strings to one string that causes in resulting long strings
    var xmlString = string.Join("", r);

    //Now you can load your string to a XmlDocument
    var xml = new XmlDocument();

    //Note: You need to add a root element to your result
    xml.LoadXml($"<root>{xmlString}</root>");
}

Note: To get records from your stored procedure you need to add SET NOCOUNT ON; after BEGIN ;).

CREATE PROCEDURE [dbo].[xml_test] 
AS
BEGIN
    SET NOCOUNT ON;

    SELECT * from dbo.AspNetUsers FOR XML RAW;
END

Database.ExecuteSqlCommand executes commands used for CRUD operation, not querying.

Using Database.SqlQuery is for queries. It will return elements of a given type but xml isn't a primitive type and probably it's a reason why LINQ not working. Try cast xml in stored procedure to nvarchar(max) this will be string type.

. So your stored procedure should look like:

    CREATE PROCEDURE xml_test    
AS
BEGIN
    DECLARE @xml1 xml

    SET @xml1 = (SELECT * from Product FOR XML RAW) 

    SELECT CAST(@xml1 as nvarchar(max))
END

As mentioned by shA.t " FOR XML " can be used. However one thing to take care of while using is the truncation of string/XML (returned after function call via EF) at around 2k characters, to handle this scenario you can have a look at this . Also if the code design allows, you can even use Ouptput parameters with Enitity Framework .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM