既知のパラメーターセットを使用してDapperでクエリを実行しようとしていますが、それらのパラメーターの値のリストを使用しています。私がやろうとしていることの簡単な例は次のようになります:
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は「パラメーター '@Status'を定義する必要があります」という例外をスローします。 Dapperが一括挿入および更新を実行するときにパラメーターのリストを処理できることを知っていますが、選択に対してこれを実行できませんか?
ああ、私はあなたが何を意味するか理解していると思います...
はい、クエリでサポートされていないExecute
をサポートするシナリオがあります。具体的には、さまざまなパラメーター値の範囲で同じ操作を順次実行します。これはExecute
には意味がありますが、クエリの場合、in
を使用して別のクエリを調べる必要があることを意味します。または、ループして連結するだけです。
代わりに、単一のパラメーターオブジェクトとパブリック値を探しています。列挙型には、dapperに適したパラメーター値がありません。
これを試して:
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});
私はこのパーティーに遅れていることを知っていますが、このリクエストは、いくつかのプロパティを渡して、それらの動的プロパティに基づいてクエリを生成したいということを意味すると理解していると思います。
以下のコードを使用すると、任意のタイプを使用して、いくつかの値が設定されたそのタイプのオブジェクトを入力して渡すだけで(これを自分のクエリオブジェクトと呼びます)、クエリが生成され、クエリオブジェクトに設定します。
*ブール値とデフォルト値を持つものに注意してください。
動的クエリの例
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();
}
使用方法
* repoは上記の関数を含むクラスです
var blah = repo.Query<Domain>(new Domain() { Id = 1, IsActive=true });
出力
SELECT * From Domain Where Id = @Id AND IsActive = @IsActive
次に、上記のクエリに一致する「ドメイン」をすべて吐き出します。
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
それが私が行う方法ですが、これは1つのことを前提としています。「Numbers」という名前のデータベースに、1行から1つずつ、任意の数の整数があり、1から始まり、少なくとも24個の数値があるテーブルがあるとします。
つまり、テーブルは次のようになります。
n
-----
1
2
3
4
5
...
そのようなテーブルがない場合は、このコマンドのためだけに作成するのが非常に高速で簡単です。
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.
ストアドプロシージャに複数のバッチを含めることはできませんが、テキストコマンドには含めることができます。 GO 16
は、前のバッチを16回実行します。ストアドプロシージャでこれが必要な場合は、バッチを使用する代わりに、2番目のINSERT
コマンドを何度も繰り返すことができます。この特定のクエリでは2 ^ 16整数はやり過ぎですが、必要なときにコピーして貼り付けるコマンドであり、通常は2 ^ 16で十分であり、非常に高速なので、通常は変更する必要がありません。 GO 5
は32の整数を生成します。これは24の日付範囲に十分です。
この機能を説明するスクリプト全体を次に示します。
--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.
*/