【问题标题】:Passing and return values from stored procedure to view model从存储过程传递和返回值到视图模型
【发布时间】:2018-08-03 09:48:40
【问题描述】:

提前,我很欣赏 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,这意味着它什么也没返回或有错误?

非常感谢您的帮助

德里克

【问题讨论】:

  • 您使用的是哪个 dbms? (该代码是特定于产品的。)
  • 如果没有行受到影响,存储过程将返回-1(因为您使用的是SET NOCOUNT ON,相关问题here)。如果要将结果投影到模型类中,请改用_context.Database.SqlQuery<TResult>("exec uspGetOrgChart", personIdParam);
  • 我用的是SQLServer,我用的管理软件是MS SQL Studio
  • 为什么不使用带有表格结果的函数并对其进行选择?像这样:_context.Database.SqlQuery("SELECT Name, Login FROM Users()").ToList();更多关于这些功能的信息:stackoverflow.com/questions/440308/…

标签: c# sql-server stored-procedures asp.net-core-mvc viewmodel


【解决方案1】:

过程不能返回表。使用带有表返回值的替代函数:

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 doc 了解关于表格结果的函数

然后调用

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

山本哲也建议

否则您必须调用存储来填充临时表,然后从临时表中选择数据并刷新它

注意。您可以轻松地将参数传递给这些函数。为简洁省略

【讨论】:

    【解决方案2】:

    Database.ExecuteSqlCommand 方法通常用于执行操作 DML 查询(INSERTUPDATEDELETE),它从这些操作返回受影响的行数。如果您想将SELECT 查询结果返回到指定的模型类,您应该使用Database.SqlQuery&lt;TElement&gt; 并将目标模型类设置为类型参数,如下所示:

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

    注意:默认情况下,如果没有受ExecuteSqlCommand影响的记录/行,则返回-1值。

    其他参考:Executing Raw SQL Queries Using Entity Framework

    更新:

    对于 Entity Framework Core,Database.SqlQuery&lt;TElement&gt; 不可用,您可以使用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
                }
            }
        }
    }
    

    相关问题:

    How to run stored procedures in Entity Framework Core?

    Database.SqlQuery Missing

    【讨论】:

    • 感谢您到目前为止的回复和帮助,看来我可能在做一些断章取义的事情,因为我没有可用的 SqlQuery 方法,我可能缺少参考吗?
    • 如果您正在使用 EntityFramework 并且您的上下文正在扩展 DbContext 类,您应该将其视为数据库属性的方法。可能是你的智能没了。尝试打开和关闭 vs.
    【解决方案3】:

    到目前为止,谢谢大家

    谢谢你,这看起来离工作更近了。当我使用代码时

    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 
    

    并在整个应用程序的其余部分中使用,我真的必须创建一个全新的上下文和连接才能运行该过程。

    谢谢 德里克

    【讨论】:

      【解决方案4】:

      感谢大家的帮助,我设法让 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”

      【讨论】:

        【解决方案5】:

        你可以使用这个解决方案:

         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;
                }
        

        【讨论】:

        • 我尝试在实体框架核心中调用存储过程。在使用部分我调用存储过程。这个sp返回category模型列表,MapToList方法将结果映射到Category类
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-11-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-03-10
        • 1970-01-01
        相关资源
        最近更新 更多