【问题标题】:How to execute stored procedure with input and output parameters using EF?如何使用 EF 执行带有输入和输出参数的存储过程?
【发布时间】:2018-02-19 12:25:11
【问题描述】:

我使用的是 EF 代码优先方法,并且我使用迁移创建了存储过程,如下所示:

public override void Up()
    {
        Sql(@"CREATE TYPE IdsList AS TABLE   
                    ( 
                    Id Int
                    )
                    GO

                    Create Procedure getChildIds(
                    @IdsList dbo.IdsList ReadOnly
                    )
                    As
                    Begin
                    WITH RecursiveCTE AS
                    (
                        SELECT Id
                        FROM dbo.PhysicalObjects
                        WHERE ParentId in (Select * from @IdsList)
                        --Where Id=108
                        UNION ALL

                        SELECT t.Id
                        FROM dbo.PhysicalObjects t
                        INNER JOIN RecursiveCTE cte ON t.ParentId = cte.Id
                    )
                    SELECT * FROM RecursiveCTE
                    End");
    }

    public override void Down()
    {
        Sql(@"Drop Type IdsList
                Go
                Drop Procedure getChildIds");
    }

现在如果我去 sql server management studio 并执行以下脚本:

Declare @Ids dbo.IdsList

Insert into @Ids
SELECT 1

Exec getChildIds @Ids

它将成功执行,但现在我正在尝试执行该存储过程,如下所示:

 var idsList = new SqlParameter {ParameterName = "idsList",  Value = new int[] { 1,2,3,4,5} };

 var idParams = new SqlParameter("idParams", SqlDbType.Structured)
            {
                Direction = System.Data.ParameterDirection.Output
            };

var results = dbContext.Database.SqlQuery<int>("getChildIds @idsList, @idParams out", idsList,idParams) ;

var idsResult = (List<int>)idParams.Value;

它不返回任何东西。

那么我怎样才能使用 Table 类型的输入和输出参数来执行存储过程呢?

【问题讨论】:

  • 我在您的 SP 中没有看到 OUTPUT 参数;只有一个输入参数 (@IdsList)。相反,您的 SP 正在返回一个数据集。
  • 我应该修改我的存储过程吗?
  • 是的,你需要声明一个 OUTPUT 变量。
  • 我认为这里不需要使用OUTPUT 参数。就个人而言,我会改为更改您的应用程序 (C#) 代码以期望返回 DataTable。您可以使用 OUTPUT 参数,但这似乎是不必要的复杂性,无论如何您都可能在应用程序中将其转换为 Datatable
  • 老实说,我不太擅长编写 C#。我可以很好地阅读它,因此为什么我可以看到问题,但是,我总是用 VB.Net 编写的(要阅读,语言并不太相似)。我还没有处于需要学习/使用 C# 的位置,因此我还没有学会如何编写它。

标签: c# sql-server entity-framework stored-procedures


【解决方案1】:
【解决方案2】:

我是这样解决的。

首先,我更新了我的存储过程以返回 ID,如下所示:

 public override void Up()
    {
        Sql(@"CREATE TYPE IdsList AS TABLE   
                    ( 
                    Id Int
                    )
                    GO

                    Create Procedure getChildIds(
                    @IdsList dbo.IdsList ReadOnly
                    )
                    As
                    Begin
                    WITH RecursiveCTE AS
                    (
                        SELECT Id
                        FROM dbo.PhysicalObjects
                        WHERE ParentId in (Select * from @IdsList)
                        UNION ALL

                        SELECT t.Id
                        FROM dbo.PhysicalObjects t
                        INNER JOIN RecursiveCTE cte ON t.ParentId = cte.Id
                    )
                    SELECT Id From RecursiveCTE
                    End");
    }

    public override void Down()
    {
        Sql(@" Drop Procedure getChildIds
               Go
               Drop Type IdsList
               ");
    }

这里是我如何解决使用实体框架执行存储过程的:

 var dataTable = new DataTable();
 dataTable.TableName = "idsList";
 dataTable.Columns.Add("Id", typeof(int));
 dataTable.Rows.Add(1);
 dataTable.Rows.Add(2);

  SqlParameter idsList = new SqlParameter("idsList", SqlDbType.Structured);
  idsList.TypeName = dataTable.TableName;
  idsList.Value = dataTable;

  var results = dbContext.Database.SqlQuery<int>("exec getChildIds @idsList", idsList).ToList();

我希望我的代码能帮助其他遇到同样问题的人

【讨论】:

    【解决方案3】:

    在 SQL 中使用用户的过程/函数和内置函数的最佳解决方案是这个库 - https://github.com/Dixin/EntityFramework.Functions

    它具有出色且易于实现的功能。它还允许遵循代码优先的方法

    在您的情况下,您需要使用表值函数

    定义模型

    [ComplexType]
    public class IdModel
    {
         public int Id { get; set; }
    }
    

    在 DbContext 中声明类型和约定

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Conventions.Add(new FunctionConvention<DbContext>());
    
        modelBuilder.ComplexType<IdModel>();
    
        base.OnModelCreating(modelBuilder);
    }
    

    在 DbContext 中定义函数

    [Function(FunctionType.TableValuedFunction, nameof(ufnGetChildIds), Schema = "dbo")]
    public IQueryable<IdModel> ufnGetChildIds([Parameter(DbType = "IdModel", Name = "IdsList")]List<IdModel> IdsList)
    {
        ObjectParameter IdsListParameter = new ObjectParameter("IdsList", IdsList);
    
        return this.ObjectContext().CreateQuery<IdModel>(
            $"[{nameof(this.ufnGetChildIds)}](@{nameof(IdsList)})", IdsListParameter);
    }
    

    并从您的 DbContext 中使用它

    [TestMethod]
    public void CallTableValuedFunction()
    {
        using (DbContext database = new DbContext())
        {
            IQueryable<IdModel> employees = database.ufnGetChildIds(new List<IdModel> { new IdModel { Id = 1 }});
        }
    }
    

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-27
    • 1970-01-01
    • 2010-12-08
    • 1970-01-01
    • 2016-03-24
    • 1970-01-01
    相关资源
    最近更新 更多