Skip to content

Commit af7938c

Browse files
dev: DapperIntegrationTests.cs (#184)
1 parent 314a874 commit af7938c

File tree

5 files changed

+233
-21
lines changed

5 files changed

+233
-21
lines changed

examples/src/DapperExample/Program.cs

Lines changed: 16 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3,28 +3,25 @@
33
using Dapper;
44
using Ydb.Sdk.Ado;
55

6-
// TODO SQL Parser @Id -> $Id
7-
86
// Init Users table
9-
await new YdbConnection().ExecuteAsync("""
10-
CREATE TABLE Users(
11-
Id Int32,
12-
Name Text,
13-
Email Text,
14-
PRIMARY KEY (Id)
15-
);
16-
""");
17-
18-
await new YdbConnection().ExecuteAsync("INSERT INTO Users(Id, Name, Email) VALUES ($Id, $Name, $Email)",
19-
new Dictionary<string, object> { { "$Id", 1 }, { "$Name", "Name" }, { "$Email", "Email" } });
20-
21-
Console.WriteLine(await new YdbConnection().QuerySingleAsync<User>("SELECT * FROM Users WHERE Id = $Id",
22-
new Dictionary<string, object> { { "$Id", 1 } }));
23-
24-
Console.WriteLine(await new YdbConnection().QuerySingleAsync<User>("SELECT * FROM Users WHERE Id = $Id",
7+
await using var connection = await new YdbDataSource().OpenConnectionAsync();
8+
9+
await connection.ExecuteAsync("""
10+
CREATE TABLE Users(
11+
Id Int32,
12+
Name Text,
13+
Email Text,
14+
PRIMARY KEY (Id)
15+
);
16+
""");
17+
18+
await connection.ExecuteAsync("INSERT INTO Users(Id, Name, Email) VALUES ($Id, $Name, $Email)",
19+
new User { Id = 1, Name = "Name", Email = "Email" });
20+
21+
Console.WriteLine(await connection.QuerySingleAsync<User>("SELECT * FROM Users WHERE Id = $Id",
2522
new { Id = 1 }));
2623

27-
await new YdbConnection().ExecuteAsync("DROP TABLE Users");
24+
await connection.ExecuteAsync("DROP TABLE Users");
2825

2926
internal class User
3027
{

src/Ydb.Sdk/src/Ado/YdbCommand.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,11 +168,13 @@ protected override YdbDataReader ExecuteDbDataReader(CommandBehavior behavior)
168168
protected override async Task<DbDataReader> ExecuteDbDataReaderAsync(CommandBehavior behavior,
169169
CancellationToken cancellationToken)
170170
{
171-
if (YdbConnection.LastReader is { IsClosed: false })
171+
if (YdbConnection.IsBusy)
172172
{
173173
throw new YdbOperationInProgressException(YdbConnection);
174174
}
175175

176+
YdbConnection.EnsureConnectionOpen();
177+
176178
var ydbParameters = DbParameterCollection.YdbParameters;
177179
var (sql, paramNames) = SqlParser.Parse(CommandText);
178180
var preparedSql = new StringBuilder();

src/Ydb.Sdk/src/Ado/YdbConnection.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ protected override YdbCommand CreateDbCommand()
164164
return CreateDbCommand();
165165
}
166166

167-
private void EnsureConnectionOpen()
167+
internal void EnsureConnectionOpen()
168168
{
169169
if (ConnectionState == ConnectionState.Closed)
170170
{
Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,212 @@
1+
using System.ComponentModel.DataAnnotations.Schema;
2+
using System.Data;
3+
using Dapper;
4+
using Xunit;
5+
using Ydb.Sdk.Ado;
6+
7+
namespace Ydb.Sdk.Tests.Dapper;
8+
9+
public class DapperIntegrationTests
10+
{
11+
private static readonly TemporaryTables<DapperIntegrationTests> Tables = new();
12+
13+
[Fact]
14+
public async Task DapperYqlTutorialTests()
15+
{
16+
SqlMapper.SetTypeMap(
17+
typeof(Episode),
18+
new CustomPropertyTypeMap(
19+
typeof(Episode),
20+
(type, columnName) =>
21+
type.GetProperties().FirstOrDefault(prop =>
22+
prop.GetCustomAttributes(false)
23+
.OfType<ColumnAttribute>()
24+
.Any(attr => attr.Name == columnName)) ?? throw new InvalidOperationException()));
25+
26+
await using var connection = new YdbConnection();
27+
await connection.OpenAsync();
28+
29+
await connection.ExecuteAsync(Tables.CreateTables); // create tables
30+
await connection.ExecuteAsync(Tables.UpsertData); // adding data to table
31+
32+
var selectedEpisodes = (await connection.QueryAsync<Episode>($@"
33+
SELECT
34+
series_id,
35+
season_id,
36+
episode_id,
37+
title,
38+
air_date
39+
40+
FROM {Tables.Episodes}
41+
WHERE
42+
series_id = @series_id -- List of conditions to build the result
43+
AND season_id > @season_id -- Logical AND is used for complex conditions
44+
45+
ORDER BY -- Sorting the results.
46+
series_id, -- ORDER BY sorts the values by one or multiple
47+
season_id, -- columns. Columns are separated by commas.
48+
episode_id
49+
50+
LIMIT 3 -- LIMIT N after ORDER BY means
51+
-- ""get top N"" or ""get bottom N"" results,
52+
; -- depending on sort order.
53+
", new { series_id = 1, season_id = 1 })).ToArray();
54+
55+
Assert.Equal(
56+
new[]
57+
{
58+
new Episode
59+
{
60+
SeriesId = 1, SeasonId = 2, EpisodeId = 1, Title = "The Work Outing",
61+
AirDate = new DateTime(2006, 8, 24)
62+
},
63+
new Episode
64+
{
65+
SeriesId = 1, SeasonId = 2, EpisodeId = 2, Title = "Return of the Golden Child",
66+
AirDate = new DateTime(2007, 8, 31)
67+
},
68+
new Episode
69+
{
70+
SeriesId = 1, SeasonId = 2, EpisodeId = 3, Title = "Moss and the German",
71+
AirDate = new DateTime(2007, 9, 7)
72+
}
73+
}, selectedEpisodes);
74+
75+
76+
var selectedTitlesSeasonAndSeries = (await connection.QueryAsync<dynamic>($@"
77+
SELECT
78+
sa.title AS season_title, -- sa and sr are ""join names"",
79+
sr.title AS series_title, -- table aliases declared below using AS.
80+
sr.series_id, -- They are used to avoid
81+
sa.season_id -- ambiguity in the column names used.
82+
83+
FROM
84+
{Tables.Seasons} AS sa
85+
INNER JOIN
86+
{Tables.Series} AS sr
87+
ON sa.series_id = sr.series_id
88+
WHERE sa.series_id = @series_id
89+
ORDER BY -- Sorting of the results.
90+
sr.series_id,
91+
sa.season_id -- ORDER BY sorts the values by one column
92+
; -- or multiple columns.
93+
-- Columns are separated by commas.", new { series_id = 1 })).ToArray();
94+
95+
for (var i = 0; i < selectedTitlesSeasonAndSeries.Length; i++)
96+
{
97+
Assert.Equal("IT Crowd", selectedTitlesSeasonAndSeries[i].series_title);
98+
Assert.Equal("Season " + (i + 1), selectedTitlesSeasonAndSeries[i].season_title);
99+
}
100+
101+
var transaction = connection.BeginTransaction();
102+
var episode1 = new Episode
103+
{ SeriesId = 2, SeasonId = 5, EpisodeId = 13, Title = "Test Episode", AirDate = new DateTime(2018, 8, 27) };
104+
var episode2 = new Episode
105+
{
106+
SeriesId = 2, SeasonId = 5, EpisodeId = 12, Title = "Test Episode !!!", AirDate = new DateTime(2018, 8, 27)
107+
};
108+
109+
var parameters1 = new DynamicParameters();
110+
parameters1.Add("series_id", episode1.SeriesId, DbType.UInt64);
111+
parameters1.Add("season_id", episode1.SeasonId, DbType.UInt64);
112+
parameters1.Add("episode_id", episode1.EpisodeId, DbType.UInt64);
113+
parameters1.Add("title", episode1.Title, DbType.String);
114+
parameters1.Add("air_date", episode1.AirDate, DbType.Date);
115+
116+
await connection.ExecuteAsync($@"
117+
UPSERT INTO {Tables.Episodes}
118+
(
119+
series_id,
120+
season_id,
121+
episode_id,
122+
title,
123+
air_date
124+
)
125+
VALUES
126+
(
127+
@series_id,
128+
@season_id,
129+
@episode_id,
130+
@title,
131+
@air_date
132+
);
133+
;", parameters1, transaction);
134+
await using (var otherConn = new YdbConnection())
135+
{
136+
await otherConn.OpenAsync();
137+
138+
Assert.Null(await otherConn.QuerySingleOrDefaultAsync(
139+
$"SELECT * FROM {Tables.Episodes} WHERE series_id = @p1 AND season_id = @p2 AND episode_id = @p3",
140+
new { p1 = episode1.SeriesId, p2 = episode1.SeasonId, p3 = episode1.EpisodeId }));
141+
}
142+
143+
var parameters2 = new DynamicParameters();
144+
parameters2.Add("series_id", episode2.SeriesId, DbType.UInt64);
145+
parameters2.Add("season_id", episode2.SeasonId, DbType.UInt64);
146+
parameters2.Add("episode_id", episode2.EpisodeId, DbType.UInt64);
147+
parameters2.Add("title", episode2.Title, DbType.String);
148+
parameters2.Add("air_date", episode2.AirDate, DbType.Date);
149+
150+
await connection.ExecuteAsync($@"
151+
UPSERT INTO {Tables.Episodes}
152+
(
153+
series_id,
154+
season_id,
155+
episode_id,
156+
title,
157+
air_date
158+
)
159+
VALUES
160+
(
161+
@series_id,
162+
@season_id,
163+
@episode_id,
164+
@title,
165+
@air_date
166+
);
167+
;", parameters2, transaction);
168+
await transaction.CommitAsync();
169+
170+
var rollbackTransaction = connection.BeginTransaction();
171+
await connection.ExecuteAsync($@"
172+
INSERT INTO {Tables.Episodes}
173+
(
174+
series_id,
175+
season_id,
176+
episode_id,
177+
title,
178+
air_date
179+
)
180+
VALUES
181+
(
182+
2,
183+
5,
184+
21,
185+
""Test 21"",
186+
Date(""2018-08-27"")
187+
), -- Rows are separated by commas.
188+
(
189+
2,
190+
5,
191+
22,
192+
""Test 22"",
193+
Date(""2018-08-27"")
194+
)
195+
;
196+
;", transaction: rollbackTransaction);
197+
await rollbackTransaction.RollbackAsync();
198+
199+
Assert.Equal((ulong)72, await connection.ExecuteScalarAsync<ulong>($"SELECT COUNT(*) FROM {Tables.Episodes}"));
200+
201+
await connection.ExecuteAsync(Tables.DeleteTables);
202+
}
203+
204+
private record Episode
205+
{
206+
[Column("series_id")] public uint SeriesId { get; set; }
207+
[Column("season_id")] public uint SeasonId { get; set; }
208+
[Column("episode_id")] public uint EpisodeId { get; set; }
209+
[Column("title")] public string Title { get; set; } = null!;
210+
[Column("air_date")] public DateTime AirDate { get; set; }
211+
}
212+
}

src/Ydb.Sdk/tests/Tests.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
</PropertyGroup>
1717

1818
<ItemGroup>
19+
<PackageReference Include="Dapper" Version="2.1.35" />
1920
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="8.0.0-rc.1.23419.4" />
2021
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
2122
<PackageReference Include="Moq" Version="4.20.70" />

0 commit comments

Comments
 (0)