Заранее я ценю, что в Stackoverflow есть много вопросов и ответов, связанных с хранимыми процедурами, я знаю, потому что за последние несколько дней я прочитал тонну из них, но я все еще не могу понять процесс создания хранимой процедуры который вернет значения в мою модель представления.
Этот вопрос состоит из двух частей:
Я очень ценю любую помощь, которую может мне оказать сообщество ...
DiscoveryPersonuspGetOrgChartМой стол
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, что означает, что он ничего не вернул или какая-то ошибка?
Большое спасибо за любую помощь
Дерек
Хранимая процедура возвращает -1, если никакие строки не затронуты (поскольку вы используете SET NOCOUNT ON, связанную проблему здесь). Если вы хотите спроецировать результат в класс модели, используйте вместо этого _context.Database.SqlQuery<TResult>("exec uspGetOrgChart", personIdParam);.
Я использую SQLServer, программное обеспечение для управления, которое я использую, - это MS SQL Studio.
почему бы вам не использовать функцию с табличным результатом и не сделать на ней выбор? Примерно так: _context.Database.SqlQuery <User> ("ВЫБРАТЬ имя, войти от пользователей ()"). ToList (); Подробнее об этих функциях здесь: stackoverflow.com/questions/440308/…





Метод 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?
Спасибо за ответ и помощь, кажется, я делаю что-то вне контекста, потому что у меня нет доступного мне метода SqlQuery, возможно, мне не хватает ссылки?
Если вы используете EntityFramework и ваш контекст расширяет класс DbContext, вы должны видеть его как метод свойства Database. Может быть, ваш интеллект ушел. Попробуйте открыть и закрыть vs.
Процедура не может вернуть таблицу. Вместо этого используйте функцию с возвращаемым значением таблицы:
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.
Какие СУБД вы используете? (Этот код зависит от продукта.)