C# 如何使用EF执行带有输入和输出参数的存储过程?

C# 如何使用EF执行带有输入和输出参数的存储过程?,c#,sql-server,entity-framework,stored-procedures,C#,Sql Server,Entity Framework,Stored Procedures,我使用EF代码优先的方法,并使用迁移创建了存储过程,如下所示: public override void Up() { Sql(@"CREATE TYPE IdsList AS TABLE ( Id Int ) GO Create Procedure getChil

我使用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");
    }
 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;
 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
               ");
    }
现在,如果我转到sql server management studio并执行以下脚本:

Declare @Ids dbo.IdsList

Insert into @Ids
SELECT 1

Exec getChildIds @Ids
它将成功执行,但现在我尝试按如下方式执行该存储过程:

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");
    }
 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;
 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 idsList=newsqlparameter{ParameterName=“idsList”,Value=newint[]{1,2,3,4,5};
var idParams=新的SqlParameter(“idParams”,SqlDbType.Structured)
{
方向=System.Data.ParameterDirection.Output
};
var results=dbContext.Database.SqlQuery(“getChildIds@idsList,@idParams out”,idsList,idParams);
var idsResult=(列表)idParams.Value;
它不返回任何内容


那么,如何使用Table类型的输入和输出参数执行存储过程呢?

您必须以不同的方式获取数据:


我用这种方法解决了这个问题

首先,我更新了存储过程以返回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)
                        --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");
    }
 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;
 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();
var dataTable=new dataTable();
dataTable.TableName=“idsList”;
dataTable.Columns.Add(“Id”,typeof(int));
dataTable.Rows.Add(1);
dataTable.Rows.Add(2);
SqlParameter idsList=新的SqlParameter(“idsList”,SqlDbType.Structured);
idsList.TypeName=dataTable.TableName;
idsList.Value=数据表;
var results=dbContext.Database.SqlQuery(“exec getChildIds@idsList”,idsList).ToList();

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

在SQL中使用用户过程/函数和内置函数的最佳解决方案是这个库-

它的实现非常简单。它还允许遵循代码优先的方法

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

定义模型

[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);
}
[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);
}
[TestMethod]
public void CallTableValuedFunction()
{
    using (DbContext database = new DbContext())
    {
        IQueryable<IdModel> employees = database.ufnGetChildIds(new List<IdModel> { new IdModel { Id = 1 }});
    }
}
模型创建时受保护的覆盖无效(DbModelBuilder modelBuilder)
{
添加(新函数convention());
modelBuilder.ComplexType();
基于模型创建(modelBuilder);
}
在DbContext中定义函数

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

    modelBuilder.ComplexType<IdModel>();

    base.OnModelCreating(modelBuilder);
}
[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);
}
[TestMethod]
public void CallTableValuedFunction()
{
    using (DbContext database = new DbContext())
    {
        IQueryable<IdModel> employees = database.ufnGetChildIds(new List<IdModel> { new IdModel { Id = 1 }});
    }
}
[函数(FunctionType.TableValuedFunction,name of(ufngetchildis),Schema=“dbo”)]
公共IQueryable ufnGetChildIds([参数(DbType=“IdModel”,Name=“IdsList”)]列表IdsList)
{
ObjectParameter IdsListParameter=新的ObjectParameter(“IdsList”,IdsList);
返回此.ObjectContext().CreateQuery(
$“[{nameof(this.ufngetchildis)}](@{nameof(IdsList)})”,IdsListParameter);
}
并从DbContext中使用它

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

    modelBuilder.ComplexType<IdModel>();

    base.OnModelCreating(modelBuilder);
}
[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);
}
[TestMethod]
public void CallTableValuedFunction()
{
    using (DbContext database = new DbContext())
    {
        IQueryable<IdModel> employees = database.ufnGetChildIds(new List<IdModel> { new IdModel { Id = 1 }});
    }
}
[TestMethod]
public void CallTableValuedFunction()
{
使用(DbContext数据库=新的DbContext())
{
IQueryable employees=database.ufngetchildis(新列表{new-IdModel{Id=1}});
}
}

我在您的SP中没有看到
输出
参数;只有一个输入参数(
@IdsList
)。而您的SP正在返回一个数据集。我是否应该修改存储过程?是的,您需要声明一个输出变量。不过,我认为没有必要在此处使用
OUTPUT
参数。就我个人而言,我宁愿更改您的应用程序(C#)代码,以期望返回一个
DataTable
。您可以使用
输出
参数,但这似乎是不必要的复杂性,您可能会在应用程序中将其转换为
数据表
。老实说,我不太擅长编写C。我可以很好地阅读它,因此我可以看到这个问题,然而,我总是用VB.Net编写(要阅读,语言并没有太大的不同)。我还没有处于需要学习/使用C#的位置,因此我还没有学会如何编写C#。Igor,请不要只是发布一些工具或库作为答案。至少要在答案本身上加以证明。