我在我的应用程序中使用Asp.net核心和EF核心。基本上我想从单个存储过程中获取多个结果集。试图搜索它最近2天没有这样的运气。试图弄清楚解决它的工作..
这是我的存储过程:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[usp_CustomerAll_sel]
@SomeOutput int OUT
AS
BEGIN
SET NOCOUNT ON;
SELECT *
FROM [dbo].[Customer]
SELECT @SomeOutput = @@rowcount + 25 --This number 25 is a variable from a complex query but always an integer
END
我在该表中有2条记录,所以基本上它应该返回一个客户类型表,输出参数应该返回27 ..
现在从我的.net代码到目前为止我尝试过了
[HttpGet]
public async Task<Tuple<IEnumerable<Customer>, int>> GetAllCustomer()
{
var votesParam = new SqlParameter
{
ParameterName = "SomeOutput",
Value = -1,
Direction = ParameterDirection.Output
};
var y = await _customerContext.Customers.FromSql("usp_CustomerAll_sel @SomeOutput out", votesParam).ToArrayAsync();
return new Tuple<IEnumerable<Customer>, int>(y, (int)votesParam.Value);
}
上面的一个返回列表,但我没有从DB .(int)votesParam.Value
得到输出参数的值显示为空
现在,如果我使用ExecuteNonQueryAsync
,那么我将获得输出参数,而不是实际数据
private async Task ExecuteStoredProc()
{
DbCommand cmd = _customerContext.Database.GetDbConnection().CreateCommand();
cmd.CommandText = "dbo.usp_CustomerAll_sel";
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add(new SqlParameter("@SomeOutput", SqlDbType.BigInt) { Direction = ParameterDirection.Output, Value = -1 });
if (cmd.Connection.State != ConnectionState.Open)
{
cmd.Connection.Open();
}
await cmd.ExecuteNonQueryAsync();
long SomeOutput = (long)cmd.Parameters["@SomeOutput"].Value;
}
有没有办法同时获得结果集和输出参数并作为元组返回?
当我输入硬编码值时,它看起来像
[HttpGet]
public async Task<Tuple<IEnumerable<Customer>, int>> GetAllCustomer()
{
var votesParam = new SqlParameter
{
ParameterName = "SomeOutput",
Value = -1,
Direction = ParameterDirection.Output
};
var y = await _customerContext.Customers.FromSql("usp_CustomerAll_sel @SomeOutput out", votesParam).ToArrayAsync();
return new Tuple<IEnumerable<Customer>, int>(y, **25**);
}
结果如
{"item1":[{"customerId":1,"customerName":"Cus1"},{"customerId":2,"customerName":"Cus2"}],"item2":27}
基本上这就是我要找的......有什么帮助吗?
答案 0 :(得分:1)
这应该有效。这次我只填充了一个DataTable,但你可以使用多个DataTables填充一个DataSet
using (SqlConnection connection = new SqlConnection(_customerContext.Database.Connection.ConnectionString))
{
SqlCommand cmd = new SqlCommand("dbo.usp_CustomerAll_sel", connection);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add(new SqlParameter("@SomeOutput", SqlDbType.BigInt) { Direction = ParameterDirection.Output, Value = -1 });
if (cmd.Connection.State != ConnectionState.Open)
{
cmd.Connection.Open();
}
connection.Open();
SqlDataAdapter adapter = new SqlDataAdapter(cmd);
DataTable dt = new DataTable();
adapter.Fill(dt);
long SomeOutput = (long)cmd.Parameters["@SomeOutput"].Value;
connection.Close();
}
由于您无法在.net核心中使用SqlDataAdapter,因此您可以使用第三方库来实现相同的结果,例如NReco.Data 实际上,代码非常相似。
答案 1 :(得分:1)
在EF Core中,您无法从原始SQL查询中返回特殊类型(他们正在处理此问题),因此首先,您需要针对此问题进行锻炼,将此类添加到您的项目中:
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Internal;
using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Data.Common;
using System.Linq;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
namespace Microsoft.EntityFrameworkCore
{
public static class RDFacadeExtensions
{
public static RelationalDataReader ExecuteSqlQuery(this DatabaseFacade databaseFacade, string sql, params object[] parameters)
{
var concurrencyDetector = databaseFacade.GetService<IConcurrencyDetector>();
using (concurrencyDetector.EnterCriticalSection())
{
var rawSqlCommand = databaseFacade
.GetService<IRawSqlCommandBuilder>()
.Build(sql, parameters);
return rawSqlCommand
.RelationalCommand
.ExecuteReader(
databaseFacade.GetService<IRelationalConnection>(),
parameterValues: rawSqlCommand.ParameterValues);
}
}
}
}
然后你可以调用下面的方法并从你的SP获得OUTPUT,这是一个示例:
var _sMsg = new SqlParameter("sMsg", "")
{
Direction = ParameterDirection.Output,
DbType = DbType.String,
Size = 500
};
var sql = "exec sp_foo @sUserId, @sMsg OUTPUT";
var dr = _ctx.Database.ExecuteSqlQuery(sql, _sUserID, _sMsg);
//here you can retrive your table
while (dr.DbDataReader.Read())
{
var bar = dr.DbDataReader[0].ToString();
}
//here is your OUTPUT
return _sMsg.Value.ToString();
答案 2 :(得分:0)
以下是如何使用ADO.NET从存储过程中获取多个结果集的示例:
Return multiple recordsets from stored proc in C#
但是,在这种情况下你必须抛弃输出参数。
答案 3 :(得分:0)
我在现有数据库上构建新应用程序时遇到了非常相似的问题,因此我被迫使用存储的proc。 我发现使用ExecuteScalar()查找单个结果并使用ExecuteReader()填充列表成功。 在下面,我将添加代码片段。
public async Task<IEnumerable<vCompanyLogsheet>> GetCompanyLogsheet(object period)
{
using (var db = new TimeMachineTestContext())
{
DbCommand cmd = db.Database.GetDbConnection().CreateCommand();
cmd.CommandText = "exec @return_value = dbo.[usp_GetCompanyLogSheets] 'June 2017'";
cmd.CommandType = CommandType.Text;
cmd.Parameters.Add(new SqlParameter("@return_value", SqlDbType.Int) { Direction = ParameterDirection.Output });
if (cmd.Connection.State != ConnectionState.Open)
{
cmd.Connection.Open();
}
var data = new List<vCompanyLogsheet>();
DbDataReader reader = await cmd.ExecuteReaderAsync();
if (reader.HasRows)
{
while (reader.Read())
{
data.Add(new vCompanyLogsheet()
{
TimeEntryId = reader.GetInt32(0),
TimeEntryDate = reader.GetString(1),
FullName = reader.GetString(2),
ProjectName = reader.GetString(3),
ProjectCategoryName = reader.GetString(4),
CategoryGroup = reader.GetString(5),
TimeEntryDetail = reader.GetString(6),
NrHours = reader.GetDecimal(7),
});
}
}
else
{
Console.WriteLine("No rows found.");
}
reader.Close();
if (cmd.Connection.State == ConnectionState.Open)
{
cmd.Connection.Close();
}
return data;
}
}
这是存储的proc:
CREATE PROCEDURE [dbo].[usp_GetCompanyLogSheets]
@Period varchar(50)
AS
SELECT
TimeEntryId,
CONVERT(varchar(50),TimeEntryDate,105) as TimeEntryDate,
FullName,
ProjectName,
ProjectCategoryName,
ISNULL(ProjectCategoryGroup , 'N/A') as CategoryGroup,
TimeEntryDetail,
CONVERT(DECIMAL(6,2), NrHours) as NrHours
FROM
viewTimeEntries
WHERE
Period = @Period
ORDER BY
TimeEntryDate
在SQL中执行存储的proc会创建以下代码:
DECLARE @return_value int
EXEC @return_value = [dbo].[usp_GetCompanyLogSheets]
@Period = N'June 2017'
SELECT 'Return Value' = @return_value
这似乎很简单,但是由于某种原因return_value始终为0。在返回单个整数的更基本的存储过程中,我遇到了同样的问题。我必须使用var myVar = cmd.ExecuteScalar()来获得该返回值。
我希望这会有所帮助,我花了2天的时间找到了可行的解决方案。我不知道它的效率如何,但是它会起作用,直到EF Core发布正式解决方案之前,我都会使用它。