Acessando procedimento armazenado criado dinamicamente a partir de LINQ
-
06-07-2019 - |
Pergunta
Eu estou girando dados no procedimento armazenado MS SQL. Colunas que são articuladas são criados dinamicamente usando parâmetro de procedimento armazenado (para exampe: "location1, location2, Location3") para que o número de colunas que será gerado não é conhecido. Saída deve ser semelhante (onde locais são tomadas a partir de parâmetro de procedimento armazenado):
OrderTime | location1 | Location2 | Location3
Qualquer chance de que isso pode ser usado em LINQ to SQL? Quando eu arrastei este procedimento para dbml arquivo mostra que esse procedimento retorna tipo int.
Colunas eu uso da tabela log_sales
são:
- Localização (vários local que eu estou girando),
- Carga (quantidade de dinheiro)
- OrderTime
procedimento armazenado:
CREATE PROCEDURE [dbo].[proc_StatsDay] @columns NVARCHAR(64) AS
DECLARE @SQL_PVT1 NVARCHAR(512), @SQL_PVT2 NVARCHAR(512), @SQL_FULL NVARCHAR(4000);
SET @SQL_PVT1 = 'SELECT OrderTime, ' + LEFT(@columns,LEN(@columns)-1) +'
FROM (SELECT ES.Location, CONVERT(varchar(10), ES.OrderTime, 120),ES.Charge
FROM dbo.log_sales ES
) AS D (Location,OrderTime,Charge)
PIVOT (SUM (D.Charge) FOR D.Location IN
(';
SET @SQL_PVT2 = ') )AS PVT
ORDER BY OrderTime DESC';
SET @SQL_FULL = @SQL_PVT1 + LEFT(@columns,LEN(@columns)-1) +
@SQL_PVT2;
EXEC sp_executesql @SQL_FULL, N'@columns NVARCHAR(64)',@columns = @columns
Em dbml designer.cs
arquivar o meu procedimento armazenado parte do código:
[Function(Name="dbo.proc_StatsDay")]
public int proc_EasyDay([Parameter(DbType="NVarChar(64)")] string columns)
{
IExecuteResult result = this.ExecuteMethodCall(this,((MethodInfo)MethodInfo.GetCurrentMethod())), columns);
return ((int)(result.ReturnValue));
}
Solução
Assumindo necessidade dinâmica verdadeiramente terrível, você poderia usar DataContext.ExecuteQuery
Apenas chicotear acima de um tipo que vai cobrir o espaço resultado (os nomes de propriedade devem coincidir com os nomes das colunas na consulta):
public class DynamicResult
{
public DateTime OrderDate {get;set;}
public decimal? Location1 {get;set;}
public decimal? Location2 {get;set;}
//..
public decimal? Location100 {get;set;}
}
Em seguida, chamar
IEnumerable<DynamicResult> result =
myDataContext.ExecuteQuery<DynamicResult>(commandString, param1);
Outras dicas
Você pode criar seu objeto linq para acesso após o seu conjunto de dados retornado.
Mas teria que ser realmente de qualquer uso. Linq são úteis para chamadas typesafe e resultados não dinâmicos. Você não sabe o que procurar tempo de compilação.
Você iria executar a sua instrução select
SELECT ES.Location, DateAdd(dd, DateDiff(dd, 0, ES.OrderTime), 0),ES.Charge
FROM dbo.log_sales ES
e captura o resultado para um tipo como este
public class LogSale
{
public string Location {get;set;}
public DateTime OrderDate {get;set;}
public decimal Charge {get;set;}
}
E, em seguida, "pivot" / organizar isso em memória
List<LogSale> source = LoadData();
var pivot = source
.GroupBy(ls => ls.OrderDate)
.OrderBy(g => g.Key)
.Select(g => new {
Date = g.Key,
Details = g
.GroupBy(ls => ls.Location)
.Select(loc => new {
Location = loc.Key,
Amount = loc.Sum(ls => ls.Charge)
})
});
Aqui está um segundo pivoty Linq que empurra os dados em XML em vez de tipos anônimos.
var pivot = source
.GroupBy(ls => ls.OrderDate)
.OrderBy(g => g.Key)
.Select(g => new XElement("Date",
new XAttribute("Value", g.key),
g.GroupBy(ls => ls.Location)
.Select(loc => new XElement("Detail",
new XAttribute("Location", loc.Key),
new XAttribute("Amount", loc.Sum(ls => ls.Charge))
))
));