https://github.com/adamfoneil/dapper.qx
More powerful and testable inline SQL used with Dapper
https://github.com/adamfoneil/dapper.qx
dapper-extensions inline-sql sql
Last synced: 8 months ago
JSON representation
More powerful and testable inline SQL used with Dapper
- Host: GitHub
- URL: https://github.com/adamfoneil/dapper.qx
- Owner: adamfoneil
- License: mit
- Created: 2019-11-23T14:00:03.000Z (over 5 years ago)
- Default Branch: master
- Last Pushed: 2024-02-14T12:21:14.000Z (over 1 year ago)
- Last Synced: 2024-11-10T20:45:50.340Z (8 months ago)
- Topics: dapper-extensions, inline-sql, sql
- Language: C#
- Homepage:
- Size: 226 KB
- Stars: 9
- Watchers: 2
- Forks: 3
- Open Issues: 9
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
[](https://ci.appveyor.com/project/adamosoftware/dapper-qx)
[](https://www.nuget.org/packages/Dapper.QX/)Nuget package **Dapper.QX** makes inline SQL more powerful and testable via the [Query\](https://github.com/adamosoftware/Dapper.QX/blob/master/Dapper.QX/Query_base.cs) class. Get the convenience, safety and capability of [Dapper](https://github.com/StackExchange/Dapper) with dynamic criteria, tracing, and full text queries. From the Wiki: [Why Dapper.QX?](https://github.com/adamosoftware/Dapper.QX/wiki)
```csharp
public class MyQuery : Query
{
public MyQuery() : base(
@"SELECT *
FROM [whatever]
{where}
ORDER BY [something]")
[Where("[SomeDate]>=@minDate")]
public DateTime? MinDate { get; set; }
[Where("[SomeDate]<=@maxDate")]
public DateTime? MaxDate { get; set; }
[Case("0", "[AssignedTo] IS NULL")]
[Case("-1", "[AssignedTo] IS NOT NULL")]
[Where("[AssignedTo]=@assignedTo")]
public string AssignedTo { get; set; }
}
```
Run your query like this:
```csharp
using var cn = GetConnection();
var data = await new MyQuery()
{
MinDate = DateTime.Now,
MaxDate = DateTime.Now.AddDays(30),
AssignedTo = "somebody"
}.ExecuteAsync(cn);
```
In the example above `GetConnection` is a fictional method -- you will need to provide your own method that returns an `IDbConnection` that works in your project. Read on below for an alternate syntax that lets you omit the `using` block.Use **{where}** or **{andWhere}** tokens to indicate where dynamic criteria is inserted. Mix and match [Where](https://github.com/adamosoftware/Dapper.QX/blob/master/Dapper.QX/Attributes/Where.cs) and [Case](https://github.com/adamosoftware/Dapper.QX/blob/master/Dapper.QX/Attributes/Case.cs) attributes on query class properties to control what criteria is injected. [Learn about](https://github.com/adamosoftware/Dapper.QX/wiki/Reference) more attributes Dapper.QX offers.
Note that you can omit the `using` block if you use the `Execute*` [overloads](https://github.com/adamfoneil/Dapper.QX/blob/master/Dapper.QX/Query_func.cs) that accept a `Func` instead of `IDbConnection`. This assumes you still have a method in your project that returns `IDbConnection`. Adapting the example above, this would look like this:
```csharp
var data = await new MyQuery()
{
MinDate = DateTime.Now,
MaxDate = DateTime.Now.AddDays(30),
AssignedTo = "somebody"
}.ExecuteAsync(GetConnection);
```
This approach makes sense when you have just one query to run, and you don't need the database connection for anything else.## Tooling
To help you build C# result classes for any SQL query, I offer a free tool [Postulate.Zinger](https://github.com/adamosoftware/Postulate.Zinger).[](https://aosoftware.blob.core.windows.net/install/ZingerSetup.exe)
## Testing
Make query classes testable by basing them on [TestableQuery](https://github.com/adamfoneil/Dapper.QX/blob/master/Dapper.QX/Abstract/TestableQuery.cs). This approach catches invalid SQL, but does not assert any particular query results. See the [wiki topic](https://github.com/adamfoneil/Dapper.QX/wiki/Testing-Tips) for more info.Note that you can also use the interface [ITestableQuery](https://github.com/adamfoneil/Dapper.QX/blob/master/Dapper.QX/Interfaces/ITestableQuery.cs) directly if you wish, but you must implement [TestExecute](https://github.com/adamfoneil/Dapper.QX/blob/master/Dapper.QX/Interfaces/ITestableQuery.cs#L12) yourself. There's normally no reason to do this, since I use the same [implementation](https://github.com/adamfoneil/Dapper.QX/blob/master/Dapper.QX/Abstract/TestableQuery.cs#L15) everywhere. Therefore, I recommend using the abstract class `TestableQuery` instead of `ITestableQuery`.
```csharp
public class MyQuery : TestableQuery
{
// same code above omitted
// implement GetTestCasesInner method to return every parameter combination you need to test
protected IEnumerable GetTestCasesInner()
{
yield return new MyQuery() { MinDate = DateTime.Now };
yield return new MyQuery() { MaxDate = DateTime.Now };
yield return new MyQuery() { AssignedTo = "0" };
yield return new MyQuery() { AssignedTo = "-1" };
yield return new MyQuery() { AssignedTo = "anyone" };
}
}
```
Now, in your unit test project, use the [QueryHelper.Test](https://github.com/adamfoneil/Dapper.QX/blob/master/Dapper.QX/QueryHelper_ext.cs#L16) method for each of your queries. A good way to test queries on a SQL Server localdb instance is to use my [SqlServer.LocalDb.Testing](https://github.com/adamfoneil/SqlServer.LocalDb) package. You can see how it's used in Dapper.QX's own [tests](https://github.com/adamfoneil/Dapper.QX/blob/master/Testing/ExecutionSqlServer.cs#L93).
```csharp
[TestClass]
public class QueryTests
{
private SqlConnection GetConnection()
{
// implement as needed
}[TestMethod]
public void MyQuery() => QueryHelper.Test(GetConnection);
}
```
## Table-Valued Parameters
To pass a TVP, create public property on your query class of type `DataTable` and add the `[TableType]` attribute with the name of the UDT in SQL Server it maps to. In this example, `Source` is the TVP property:
```csharp
internal class SimpleTvpExample : Query
{
public SimpleTvpExample() : base(
@"DECLARE @list [IdList];
INSERT INTO @list ([Id]) SELECT [Id] FROM @source;
SELECT [Id] FROM @list")
{
}[TableType("[dbo].[IdList]")]
public DataTable Source { get; set; }
}
```
Set the `Source` property like any other parameter. You can create a `DataTable` dynamically with the [ToDataTable](https://github.com/adamfoneil/Dapper.QX/blob/master/Dapper.QX/Extensions/EnumerableExtensions.cs#L17) extension method, as in the [test](https://github.com/adamfoneil/Dapper.QX/blob/master/Testing/ExecutionSqlServer.cs#L244):```csharp
var qry = new SimpleTvpExample() { Source = new int[] { 1, 2, 3 }.ToDataTable() };
```## Multiple WHERE clauses
You can inject multiple WHERE clauses in a query by adding a prefix on the **{where}** and **{andWhere}** tokens, making them like **{_prefix_:where}** or **{_prefix_:andWhere}**. See [issue 24](https://github.com/adamfoneil/Dapper.QX/issues/24) for more info.## Debugging
To help you debug resolved SQL, place a breakpoint on any of the `Execute*` calls, and step over that line. Look in the Debug Output window to see the resolved SQL along with any parameter declarations. You can paste this directly into SSMS and execute.
Note the extra indent you're seeing in the SQL is because of whitespace in the sample query's [source file](https://github.com/adamosoftware/Ginseng8/blob/dapper-qx/Ginseng8.Mvc/Queries/OpenWorkItems.cs#L218) from where I took this screenshot. In the source file, the SQL is stored with a verbatim string, so the indent is preserved.
Note also at this time, TVPs are not included in the debug output.