J'essaie d'exécuter une requête avec Dapper avec un jeu de paramètres connu, mais avec une liste de valeurs pour ces paramètres. Un exemple simple de ce que j'essaie de faire serait:
DateTime endDate = DateTime.Now;
DateTime startDate = endDate.AddHours(-24);
string query = "select COUNT(*) from Test where Status = @Status AND DateCreated <= @Hour;";
var stuff = con.Query(query, (startDate).ByHourTo(endDate).Select(hour => new
{
Status = 1,
Hour = hour,
}));
Dapper lève une exception avec 'Parameter' @Status 'doit être défini'. Je sais que Dapper peut traiter des listes de paramètres lors de l'insertion en bloc et des mises à jour, mais ne peut-il pas le faire pour les sélections?
Ah, je pense que je vois ce que tu veux dire ...
Oui, nous prenons en charge un scénario pour Execute
qui n'est pas pris en charge pour Query, à savoir: exécuter la même opération de manière séquentielle avec une plage de valeurs de paramètre différentes. Cela a du sens pour Execute
, mais pour requête cela signifie probablement que vous devriez examiner une requête différente en utilisant in
. Alternativement, juste en boucle et concat.
Au lieu de cela, il examine l'objet à paramètre unique et recherche des valeurs publiques - un énumérable ne possède pas de valeurs de paramètre appropriées pour dapper.
Essaye ça:
List<string> names = new List<string> { "Bob", "Fred", "Jack" };
string query = "select * from people where Name in @names";
var stuff = connection.Query<ExtractionRecord>(query, new {names});
Je sais que je suis bien en retard pour cette soirée, mais je pense avoir compris que cette demande signifiait que vous souhaitiez simplement transmettre certaines propriétés et générer votre requête en fonction de ces propriétés dynamiques.
avec le code ci-dessous, je peux utiliser n’importe quel type, puis juste remplir et transmettre un objet de ce type avec quelques valeurs définies (j’appelle cet objet de requête), et la requête sera générée pour rechercher des objets correspondant aux valeurs vous définissez dans votre objet de requête.
* Faites attention aux bools et aux choses qui ont des valeurs par défaut.
Exemple de requête dynamique
public IEnumerable<T> Query<T>(T templateobject) {
var sql = "SELECT * From " + typeof(T).Name + " Where ";
var list = templateobject.GetType().GetProperties()
.Where(p => p.GetValue(templateobject) != null)
.ToList();
int i = 0;
Dictionary<string, object> dbArgs = new Dictionary<string, object>();
list.ForEach(x =>
{
sql += x.Name + " = @" + x.Name;
dbArgs.Add(x.Name, x.GetValue(templateobject));
if (list.Count > 1 && i < list.Count - 1) {
sql += " AND ";
i++;
}
});
Debug.WriteLine(sql);
return _con.Query<T>(sql, dbArgs).ToList();
}
Utilisation
* repo est la classe qui contient la fonction ci-dessus
var blah = repo.Query<Domain>(new Domain() { Id = 1, IsActive=true });
Sortie
SELECT * From Domain Where Id = @Id AND IsActive = @IsActive
puis il crache tout "Domaines" correspondant à la requête ci-dessus.
DECLARE @Now datetime
SET @Now = getdate()
SELECT
DATEADD( hh, -n, @Now ) AS StartDate,
DATEADD( hh, -n+1, @Now ) AS EndDate
INTO
#DateRanges
FROM
Numbers
WHERE
n <= 24
SELECT
COUNT(*) AS [Count],
#DateRanges.StartDate
FROM
Test
JOIN
#DateRanges
ON Test.DateCreated >= #DateRanges.StartDate
AND Test.DateCreated < #DateRanges.EndDate
GROUP BY
#DateRanges.StartDate
C'est ce que je ferais, mais cela suppose une chose: vous avez dans votre base de données une table nommée "Numbers" qui contient un nombre arbitraire d'entiers, un par ligne, en commençant par 1, avec au moins 24 nombres.
C'est-à-dire que la table ressemble à ceci:
n
-----
1
2
3
4
5
...
Si vous ne possédez pas une telle table, il est très rapide et facile d'en créer une juste pour cette commande:
CREATE TABLE #Numbers
(
n int
)
SET NOCOUNT ON
INSERT #Numbers values (1);
GO
INSERT #Numbers SELECT n + (SELECT COUNT(*) FROM #Numbers) FROM #Numbers
GO 16 --execute batch 16 times to create 2^16 integers.
Vous ne pouvez pas avoir plusieurs lots dans une procédure stockée, mais vous pouvez le faire dans une commande de texte. GO 16
exécute le lot précédent 16 fois. Si vous en avez besoin dans une procédure stockée, vous pouvez répéter la deuxième commande INSERT
plusieurs fois au lieu d’utiliser des lots. 2 ^ 16 nombres entiers est excessif pour cette requête particulière, mais c’est une commande que je copie et colle à la demande et 2 ^ 16 est généralement suffisant, et si rapide que je n’ai généralement pas la peine de le changer. GO 5
donnerait 32 entiers, ce qui est suffisant pour 24 plages de dates.
Voici un script complet qui illustre ce travail:
--Create a temp table full of integers. This could also be a static
--table in your DB. It's very handy.
--The table drops let us run this whole script multiple times in SSMS without issue.
IF OBJECT_ID( 'tempdb..#Numbers' ) IS NOT NULL
DROP TABLE #Numbers
CREATE TABLE #Numbers
(
n int
)
SET NOCOUNT ON
INSERT #Numbers values (1);
GO
INSERT #Numbers SELECT n + (SELECT COUNT(*) FROM #Numbers) FROM #Numbers
GO 16 --execute batch 16 times to create 2^16 integers.
--Create our Test table. This would be the real table in your DB,
-- so this would not go into your SQL command.
IF OBJECT_ID( 'tempdb..#Test' ) IS NOT NULL
DROP TABLE #Test
CREATE TABLE #Test
(
[Status] int,
DateCreated datetime
)
INSERT INTO
#Test
SELECT
1,
DATEADD( hh, -n, getdate() )
FROM
#Numbers
WHERE
n <= 48
--#Test now has 48 records in it with one record per hour for
--the last 48 hours.
--This drop would not be needed in your actual command, but I
--add it here to make testing this script easier in SSMS.
IF OBJECT_ID( 'tempdb..#DateRanges' ) IS NOT NULL
DROP TABLE #DateRanges
--Everything that follows is what would be in your SQL you send through Dapper
--if you used a static Numbers table, or you might also want to include
--the creation of the #Numbers temp table.
DECLARE @Now datetime
SET @Now = getdate()
SELECT
DATEADD( hh, -n, @Now ) AS StartDate,
DATEADD( hh, -n+1, @Now ) AS EndDate
INTO
#DateRanges
FROM
#Numbers
WHERE
n <= 24
/* #DateRanges now contains 24 rows that look like this:
StartDate EndDate
2016-08-04 15:22:26.223 2016-08-04 16:22:26.223
2016-08-04 14:22:26.223 2016-08-04 15:22:26.223
2016-08-04 13:22:26.223 2016-08-04 14:22:26.223
2016-08-04 12:22:26.223 2016-08-04 13:22:26.223
...
Script was run at 2016-08-04 16:22:26.223. The first row's end date is that time.
This table expresses 24 one-hour datetime ranges ending at the current time.
It's also easy to make 24 one-hour ranges for one calendar day, or anything
similar.
*/
--Now we just join that table to our #Test table to group the rows those date ranges.
SELECT
COUNT(*) AS [Count],
#DateRanges.StartDate
FROM
#Test
JOIN
#DateRanges
ON #Test.DateCreated >= #DateRanges.StartDate
AND #Test.DateCreated < #DateRanges.EndDate
GROUP BY
#DateRanges.StartDate
/*
Since we used two different getdate() calls to populate our two tables, the last record of
our #Test table is outside of the range of our #DateRange's last row by a few milliseconds,
so we only get 23 results from this query. This script is just an illustration.
*/