Передача и возврат значений из хранимой процедуры для просмотра модели

Заранее я ценю, что в Stackoverflow есть много вопросов и ответов, связанных с хранимыми процедурами, я знаю, потому что за последние несколько дней я прочитал тонну из них, но я все еще не могу понять процесс создания хранимой процедуры который вернет значения в мою модель представления.

Этот вопрос состоит из двух частей:

  1. Почему мой код не запускается успешно и вместо этого возвращает -1?
  2. Если с вашей помощью я справлюсь с этой первой проблемой, как мне получить возвращенные данные из хранимой процедуры в список моей модели представления?

Я очень ценю любую помощь, которую может мне оказать сообщество ...

  • Имя БД - Discovery
  • Имя таблицы - Person
  • Имя хранимой процедуры - uspGetOrgChart

Мой стол

CREATE TABLE Discovery.dbo.Person 
(
     ADID nvarchar(50) NOT NULL,
     First_Name nvarchar(50) NOT NULL,
     Last_Name nvarchar(50) NOT NULL,
     Report_To_ADID nvarchar(50) NULL,
     Position_ID int NULL,
     Role_ID int NULL,
     IGEMS nvarchar(50) NULL,
     DOB date NULL,
     Start_Date date NULL,
     Cost_Code nvarchar(50) NULL,

     PRIMARY KEY CLUSTERED (ADID),
     CONSTRAINT FK_Person_Position1 
         FOREIGN KEY (Position_ID) REFERENCES dbo.Position (Position_ID),
     CONSTRAINT FK_Person_Role 
         FOREIGN KEY (Role_ID) REFERENCES dbo.Role (Role_ID)
)
ON [PRIMARY]
GO 

Моя хранимая процедура в настоящее время выглядит следующим образом:

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO

ALTER PROCEDURE [dbo].[uspGetOrgChart]
    @ContactID VARCHAR(100) = 'NOE1WWD'
AS
BEGIN
    SET NOCOUNT ON;

    --grab id of @contactid
    DECLARE @Test varchar(36)
    SELECT @Test = (SELECT ADID FROM Person c1 WHERE c1.ADID = @ContactID)

    ;WITH StaffTree AS 
    ( 
        SELECT  
            c.ADID, 
            c.First_Name, 
            c.Last_Name,
            c.Report_To_ADID, 
            c.Report_To_ADID as Manager_ID,
            cc.First_Name AS Manager_First_Name, 
            cc.Last_Name as Manager_Last_Name, 
            cc.First_name + ' ' + cc.Last_name AS [ReportsTo], 
            c.First_Name + ' ' + c.Last_Name as EmployeeName,  
            1 AS LevelOf 
        FROM 
            Person c 
        LEFT OUTER JOIN 
            Person cc ON c.Report_To_ADID = cc.ADID 
        WHERE 
            c.ADID = @Test 
            OR (@Test IS NULL AND c.Report_To_ADID IS NULL) 

        UNION ALL 

        SELECT  
            s.ADID, 
            s.First_Name, 
            s.Last_Name, 
            s.Report_To_ADID, 
            t.ADID, 
            t.First_Name, 
            t.Last_Name, 
            t.First_Name + ' ' + t.Last_Name, 
            s.First_Name + ' ' + s.Last_Name,
            t.LevelOf + 1 
        FROM 
            StaffTree t 
        INNER JOIN 
            Person s ON t.ADID = s.Report_To_ADID 
        WHERE 
            s.Report_To_ADID = @Test 
            OR @Test IS NULL 
            OR t.LevelOf > 1 
    )
    SELECT * FROM StaffTree 
END

Я протестировал, и описанная выше процедура работает так, как я ожидал, и возвращает следующие столбцы:

 ADID    
 First_Name
 Last_Name
 Report_To_ADID
 Manager_ID
 Manager_First_Name
 Manager_Last_Name
 ReportsTo
 EmployeeName
 LevelOf

Моя модель просмотра называется vmNewOrgChart:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Discovery.ViewModels
{
    public class vmNewOrgChart
    {
        public string ADID { get; set; }
        public string First_Name { get; set; }
        public string Last_Name { get; set; }
        public string Report_To_Adid { get; set; }
        public string Manager_ID { get; set; }
        public string Manager_First_Name { get; set; }
        public string Manager_Last_Name { get; set; }
        public string ReportsTo { get; set; }
        public string EmployeeName { get; set; }
        public int LevelOf { get; set; }
    }
}

Вот где я начинаю полностью застревать

public IActionResult Orgchart(string id)
{
    var personIdParam = new SqlParameter("@ContactID", SqlDbType.VarChar);
    personIdParam.Value = id;

    var result = _context.Database.ExecuteSqlCommand("exec uspGetOrgChart", personIdParam);
}

Приведенный выше код выполняется, но значение результата равно -1, что означает, что он ничего не вернул или какая-то ошибка?

Большое спасибо за любую помощь

Дерек

Какие СУБД вы используете? (Этот код зависит от продукта.)

jarlh 03.08.2018 11:49

Хранимая процедура возвращает -1, если никакие строки не затронуты (поскольку вы используете SET NOCOUNT ON, связанную проблему здесь). Если вы хотите спроецировать результат в класс модели, используйте вместо этого _context.Database.SqlQuery<TResult>("exec uspGetOrgChart", personIdParam);.

Tetsuya Yamamoto 03.08.2018 11:58

Я использую SQLServer, программное обеспечение для управления, которое я использую, - это MS SQL Studio.

Derek 03.08.2018 12:27

почему бы вам не использовать функцию с табличным результатом и не сделать на ней выбор? Примерно так: _context.Database.SqlQuery <User> ("ВЫБРАТЬ имя, войти от пользователей ()"). ToList (); Подробнее об этих функциях здесь: stackoverflow.com/questions/440308/…

Gianluca Conte 03.08.2018 12:35
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
4
3 931
5

Ответы 5

Метод Database.ExecuteSqlCommand обычно используется для выполнения запросов DML действий (INSERT, UPDATE или DELETE), который возвращает количество затронутых строк из этих операций. Если вы хотите вернуть результаты запроса SELECT в назначенный класс модели, вы должны использовать Database.SqlQuery<TElement> с целевым классом модели, установленным в качестве параметра типа следующим образом:

_context.Database.SqlQuery<vmNewOrgChart>("exec uspGetOrgChart", personIdParam);

Примечание: По умолчанию, если ExecuteSqlCommand не затрагивает ни одной записи / строки, возвращается значение -1.

Дополнительная ссылка: Выполнение сырых SQL-запросов с использованием Entity Framework

Обновлять:

Для Entity Framework Core Database.SqlQuery<TElement> недоступен, вы можете использовать DbSet.FromSql или использовать Database.GetDbConnection().CreateCommand(), как в примере ниже:

using (var context = new DataContext())
{
    using (var cmd = context.Database.GetDbConnection().CreateCommand())
    {
        cmd.CommandText = "exec uspGetOrgChart";
        cmd.CommandType = System.Data.CommandType.StoredProcedure;

        var personIdParam = cmd.CreateParameter();
        personIdParam.ParameterName = "ContactID";
        personIdParam.Value = id;

        cmd.Parameters.Add(personIdParam);

        context.Database.OpenConnection();
        using (var result = command.ExecuteReader())
        {
            if (result.HasRows)
            {
                // do something with results
            }
        }
    }
}

Связанные вопросы:

Как запустить хранимые процедуры в Entity Framework Core?

Database.SqlQuery отсутствует

Спасибо за ответ и помощь, кажется, я делаю что-то вне контекста, потому что у меня нет доступного мне метода SqlQuery, возможно, мне не хватает ссылки?

Derek 03.08.2018 13:13

Если вы используете EntityFramework и ваш контекст расширяет класс DbContext, вы должны видеть его как метод свойства Database. Может быть, ваш интеллект ушел. Попробуйте открыть и закрыть vs.

Gianluca Conte 03.08.2018 13:43

Процедура не может вернуть таблицу. Вместо этого используйте функцию с возвращаемым значением таблицы:

CREATE FUNCTION  fnGetOrgChart()
RETURNS  @rtnTable TABLE 
(
---you fields
)
AS
BEGIN
DECLARE @TempTable table (id uniqueidentifier, name nvarchar(255)....)
insert into @myTable 
select from your stuff
--This select returns data
insert into @rtnTable
SELECT ID, name FROM @mytable 
return
END

См. Документ microsoft о функции с табличным результатом

Тогда позвони

var result = context.Database.SqlQuery<vmNewOrgChart>("select * from fnGetOrgChart()");

как предложил Тэцуя Ямамото

В противном случае вы должны вызвать сохраненное, чтобы заполнить временную таблицу, затем выбрать данные из временной таблицы и сбросить ее.

NB. Вы можете легко передать параметры этой функции. Опущено для краткости

Спасибо вам всем пока

Спасибо, это похоже на работу. когда я использую код

using (_context)
{
using (var cmd = _context.Database.GetDbConnection().CreateCommand())
{
cmd.CommandText = "exec uspGetOrgChart";
cmd.CommandType = System.Data.CommandType.StoredProcedure;

var personIdParam = cmd.CreateParameter();
personIdParam.ParameterName = "ContactID";
personIdParam.Value = id;

cmd.Parameters.Add(personIdParam);

_context.Database.OpenConnection();
using (var result = cmd.ExecuteReader())
{
if (result.HasRows)
{
// do something with results

}
}
}

}

Я получаю сообщение об ошибке, что хранимая процедура не может быть найдена в этой строке, процедура определенно там и работает

(var result = cmd.ExecuteReader())

Я думал, что это потому, что код закрывался и открывал второе соединение, поэтому я закомментировал строку

_context.Database.OpenConnection();

Это привело к ошибке, что соединение не было открыто.

мой текущий контекст называется

_Context 

и используется во всем остальном приложении, действительно ли мне нужно создать совершенно новый контекст и соединение, чтобы запустить процедуру.

Спасибо Дерек

Спасибо всем за вашу помощь. Мне удалось успешно запустить Sproc, используя этот код.

using (_context)
{
                using (var cmd = _context.Database.GetDbConnection().CreateCommand())
                {
                    cmd.CommandText = "uspGetOrgChart";
                    cmd.CommandType = System.Data.CommandType.StoredProcedure;

                    var personIdParam = cmd.CreateParameter();
                    personIdParam.ParameterName = "ContactID";
                    personIdParam.Value = id;

                    cmd.Parameters.Add(personIdParam);

                    _context.Database.OpenConnection();
                    using (var result = cmd.ExecuteReader())
                    {
                        if (result.HasRows)
                        {
                            // do something with results

                        }
                    }
                }

            }

"Exec" не требуется в тексте команды.

вы можете использовать это решение:

 using (var cmd = _db.Database.GetDbConnection().CreateCommand())
                {
                    cmd.CommandText = "dbo.GetCategories"; //sp name
                    cmd.CommandType = System.Data.CommandType.StoredProcedure;
                    _db.Database.OpenConnection();
                    using (var result = cmd.ExecuteReader())
                    {
                        if (result.HasRows)
                        {
                             var List= MapToList<Category>(result);
                        }
                    }
                }

public IList<T> MapToList<T>(DbDataReader dr)
        {
            var objList = new List<T>();
            var props = typeof(T).GetRuntimeProperties();

            var colMapping = dr.GetColumnSchema()
                .Where(x => props.Any(y => y.Name.ToLower() == x.ColumnName.ToLower()))
                .ToDictionary(key => key.ColumnName.ToLower());

            if (dr.HasRows)
            {
                while (dr.Read())
                {
                    T obj = Activator.CreateInstance<T>();
                    foreach (var prop in props)
                    {
                        if (colMapping.Any(a => a.Key.ToLower() == prop.Name.ToLower()))
                        {
                            var val = dr.GetValue(colMapping[prop.Name.ToLower()].ColumnOrdinal.Value);
                            prop.SetValue(obj, val == DBNull.Value ? null : val);
                        }
                    }
                    objList.Add(obj);
                }
            }
            return objList;
        }

Я пытаюсь вызвать процедуру хранилища в ядре структуры сущности. в разделе using я вызываю хранимую процедуру. Этот sp возвращает список моделей категорий, а метод MapToList сопоставляет результат с классом Category.

Rasool Ghorbani 08.09.2019 09:53

Другие вопросы по теме