Get output parameter value of a stored procedure using EF Core?

后端 未结 4 1571
终归单人心
终归单人心 2020-12-21 01:12

I am using Asp.net core and EF core in my application. Basically I want to get multiple result set from a single stored procedure. Tried to search it for last 2 days no such

相关标签:
4条回答
  • 2020-12-21 01:23

    I had a very similar issue building a new app on an existing DB so I was forced to use the stored procs. I found success using ExecuteScalar() to find single results and ExecuteReader() to populate my lists. Below I will add snippets of my code.

    public async Task<IEnumerable<vCompanyLogsheet>> GetCompanyLogsheet(object period)
        {
            using (var db = new TimeMachineTestContext())
            {
                DbCommand cmd = db.Database.GetDbConnection().CreateCommand();
                cmd.CommandText = "exec @return_value = dbo.[usp_GetCompanyLogSheets] 'June 2017'";
                cmd.CommandType = CommandType.Text;
                cmd.Parameters.Add(new SqlParameter("@return_value", SqlDbType.Int) { Direction = ParameterDirection.Output });
                if (cmd.Connection.State != ConnectionState.Open)
                {
                    cmd.Connection.Open();
                }
    
                var data = new List<vCompanyLogsheet>();
    
                DbDataReader reader = await cmd.ExecuteReaderAsync();
    
                    if (reader.HasRows)
                    {
                        while (reader.Read())
                        {
                            data.Add(new vCompanyLogsheet()
                            {
                                TimeEntryId = reader.GetInt32(0),
                                TimeEntryDate = reader.GetString(1),
                                FullName = reader.GetString(2),
                                ProjectName = reader.GetString(3),
                                ProjectCategoryName = reader.GetString(4),
                                CategoryGroup = reader.GetString(5),
                                TimeEntryDetail = reader.GetString(6),
                                NrHours = reader.GetDecimal(7),
                            });
                        }
                    }
                    else
                    {
                        Console.WriteLine("No rows found.");
                    }
                    reader.Close();
    
                if (cmd.Connection.State == ConnectionState.Open)
                {
                    cmd.Connection.Close();
                }
    
                return data;
            }
        }
    

    And here is the stored proc:

        CREATE PROCEDURE [dbo].[usp_GetCompanyLogSheets]
        @Period varchar(50)
    AS
    SELECT
        TimeEntryId,
        CONVERT(varchar(50),TimeEntryDate,105) as TimeEntryDate,
        FullName,
        ProjectName,
        ProjectCategoryName,
        ISNULL(ProjectCategoryGroup , 'N/A') as CategoryGroup,
        TimeEntryDetail,
        CONVERT(DECIMAL(6,2), NrHours) as NrHours
    FROM
        viewTimeEntries
    WHERE
    Period = @Period
    ORDER BY
        TimeEntryDate
    

    Executing the stored proc in SQL creates the following code:

        DECLARE @return_value int
    
    EXEC    @return_value = [dbo].[usp_GetCompanyLogSheets]
            @Period = N'June 2017'
    
    SELECT  'Return Value' = @return_value
    

    It seems simple enough, but for some reason return_value is consistently 0. In more basic stored procs returning a single integer I had the same issue. I had to use var myVar = cmd.ExecuteScalar() to get that return value.

    I hope this helps, it took me like 2 days to find a working solution. I don't know how efficient it is, but it works and I'll use it until EF Core releases an official solution.

    0 讨论(0)
  • 2020-12-21 01:27

    This should work. This time I filled only a DataTable, but you can fill a DataSet with multiples DataTables

    using (SqlConnection connection  = new SqlConnection(_customerContext.Database.Connection.ConnectionString))
                    {
                        SqlCommand cmd = new SqlCommand("dbo.usp_CustomerAll_sel", connection);
                        cmd.CommandType = CommandType.StoredProcedure;
    
                        cmd.Parameters.Add(new SqlParameter("@SomeOutput", SqlDbType.BigInt) { Direction = ParameterDirection.Output, Value = -1 });
    
                        if (cmd.Connection.State != ConnectionState.Open)
                        {
                            cmd.Connection.Open();
                        }
    
    
                        connection.Open();
                        SqlDataAdapter adapter = new SqlDataAdapter(cmd);
                        DataTable dt = new DataTable();
                        adapter.Fill(dt);
    
                        long SomeOutput = (long)cmd.Parameters["@SomeOutput"].Value;
    
                        connection.Close();
                    }
    

    Since you can't use SqlDataAdapter in .net core, you can use a third party library to archieve the same result, like NReco.Data , actually, the code is pretty similar.

    0 讨论(0)
  • 2020-12-21 01:35

    Here's an example of how you can get multiple result sets from a stored procedure, if you are OK with ADO.NET:

    Return multiple recordsets from stored proc in C#

    You have to ditch the output parameter in this case, though.

    0 讨论(0)
  • 2020-12-21 01:41

    In EF Core you can't return ad-hoc types from raw SQL queries yet (they are working on this), so you will need a workaround for this issue. Add this class to your project:

    using Microsoft.EntityFrameworkCore;
    using Microsoft.EntityFrameworkCore.Infrastructure;
    using Microsoft.EntityFrameworkCore.Internal;
    using Microsoft.EntityFrameworkCore.Storage;
    using Microsoft.Extensions.DependencyInjection;
    using System;
    using System.Collections.Generic;
    using System.Data.Common;
    using System.Linq;
    using System.Reflection;
    using System.Threading;
    using System.Threading.Tasks;
    
    namespace Microsoft.EntityFrameworkCore
    {
    
        public static class RDFacadeExtensions
        {
            public static RelationalDataReader ExecuteSqlQuery(this DatabaseFacade databaseFacade, string sql, params object[] parameters)
            {
                var concurrencyDetector = databaseFacade.GetService<IConcurrencyDetector>();
    
                using (concurrencyDetector.EnterCriticalSection())
                {
                    var rawSqlCommand = databaseFacade
                        .GetService<IRawSqlCommandBuilder>()
                        .Build(sql, parameters);
    
                    return rawSqlCommand
                        .RelationalCommand
                        .ExecuteReader(
                            databaseFacade.GetService<IRelationalConnection>(),
                            parameterValues: rawSqlCommand.ParameterValues);
                }
            }
        }
    }
    

    Then you can call the method below and get the OUTPUT from you SP, here's a sample:

                var _sMsg = new SqlParameter("sMsg", "")
                {
                    Direction = ParameterDirection.Output,
                    DbType = DbType.String,
                    Size = 500
                };
    
                var sql = "exec sp_foo @sUserId, @sMsg OUTPUT";
    
                using (var dr = _ctx.Database.ExecuteSqlQuery(sql, _sUserID, _sMsg))
                {
                    //here you can retrive your table
                    while (dr.DbDataReader.Read())
                    {
                        var bar = dr.DbDataReader[0].ToString();
                    }
    
                    //here is your OUTPUT
                    return _sMsg.Value.ToString();
                }
    
    0 讨论(0)
提交回复
热议问题