Accesso alla procedura memorizzata creata dinamicamente da LINQ
-
06-07-2019 - |
Domanda
Sto ruotando i dati nella procedura memorizzata MS SQL. Le colonne pivotate vengono create dinamicamente utilizzando il parametro della procedura memorizzata (per esempio: "posizione1, posizione2, posizione3,"), quindi il numero di colonne che verranno generate non è noto. L'output dovrebbe apparire come (dove le posizioni sono prese dal parametro della procedura memorizzata):
OrderTime | Posizione1 | Posizione2 | Location3
Qualche possibilità che questo possa essere usato in LINQ to SQL? Quando ho trascinato questa procedura nel file dbml, mostra che questa procedura restituisce int type.
Le colonne che utilizzo dalla tabella log_sales
sono:
- Posizione (varie posizioni su cui sto ruotando),
- Addebito (importo del denaro)
- OrderTime
Procedura memorizzata:
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
Nel file dbml designer.cs
la mia parte di codice della procedura memorizzata:
[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));
}
Soluzione
Supponendo un'esigenza dinamica davvero terribile, è possibile utilizzare DataContext.ExecuteQuery
Basta montare un tipo che coprirà lo spazio dei risultati (i nomi delle proprietà devono corrispondere ai nomi delle colonne nella query):
public class DynamicResult
{
public DateTime OrderDate {get;set;}
public decimal? Location1 {get;set;}
public decimal? Location2 {get;set;}
//..
public decimal? Location100 {get;set;}
}
Quindi chiama
IEnumerable<DynamicResult> result =
myDataContext.ExecuteQuery<DynamicResult>(commandString, param1);
Altri suggerimenti
È possibile creare l'oggetto linq per l'accesso dopo il set di dati restituito.
Ma sarebbe davvero utile. Linq sono utili per le chiamate typesafe e non per i risultati dinamici. Non sapresti cosa cercare in fase di compilazione.
Avresti eseguito la tua dichiarazione select
SELECT ES.Location, DateAdd(dd, DateDiff(dd, 0, ES.OrderTime), 0),ES.Charge
FROM dbo.log_sales ES
e acquisisci il risultato in un tipo come questo
public class LogSale
{
public string Location {get;set;}
public DateTime OrderDate {get;set;}
public decimal Charge {get;set;}
}
E poi " pivot " / organizza quello in memoria
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)
})
});
Ecco un secondo Linq pivoty che inserisce i dati in XML anziché in tipi anonimi.
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))
))
));