Skip to content

Commit efa5ca2

Browse files
committed
add nuget package
1 parent 798ff29 commit efa5ca2

File tree

4 files changed

+436
-0
lines changed

4 files changed

+436
-0
lines changed
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<OutputType>Exe</OutputType>
5+
<TargetFramework>net8.0</TargetFramework>
6+
<ImplicitUsings>enable</ImplicitUsings>
7+
<Nullable>enable</Nullable>
8+
</PropertyGroup>
9+
<ItemGroup>
10+
<PackageReference Include="Community.Ydb.Linq2db" Version="0.0.1" />
11+
</ItemGroup>
12+
<ItemGroup>
13+
<PackageReference Include="linq2db" Version="6.0.0-rc.3" />
14+
</ItemGroup>
15+
<ItemGroup>
16+
<PackageReference Include="CommandLineParser" Version="2.8.0"/>
17+
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="5.0.0"/>
18+
<PackageReference Include="Microsoft.Extensions.Logging" Version="5.0.0"/>
19+
<PackageReference Include="Polly" Version="8.4.1" />
20+
</ItemGroup>
21+
</Project>
Lines changed: 370 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,370 @@
1+
using Linq2db.Ydb;
2+
using LinqToDB.Internal.DataProvider.Ydb;
3+
4+
namespace Linq2db.QuickStart;
5+
6+
using Microsoft.Extensions.Logging;
7+
using Polly;
8+
using LinqToDB;
9+
using LinqToDB.Async;
10+
using LinqToDB.Data;
11+
using LinqToDB.Mapping;
12+
13+
internal static class Program
14+
{
15+
public static async Task Main(string[] args)
16+
{
17+
using var factory = LoggerFactory.Create(b => b.AddConsole());
18+
var app = new AppContext(factory.CreateLogger<AppContext>());
19+
await app.Run();
20+
}
21+
}
22+
23+
#region LINQ2DB MODELS
24+
25+
[Table("series")]
26+
public sealed class Series
27+
{
28+
[PrimaryKey, Column("series_id")]
29+
public ulong SeriesId { get; set; }
30+
31+
[Column("title"), NotNull]
32+
public string Title { get; set; } = null!;
33+
34+
[Column("series_info")]
35+
public string? SeriesInfo { get; set; }
36+
37+
[Column("release_date"), DataType(DataType.Date)]
38+
public DateTime ReleaseDate { get; set; }
39+
}
40+
41+
[Table("seasons")]
42+
public sealed class Season
43+
{
44+
[PrimaryKey, Column("series_id")]
45+
public ulong SeriesId { get; set; }
46+
47+
[PrimaryKey, Column("season_id")]
48+
public ulong SeasonId { get; set; }
49+
50+
[Column("title"), NotNull]
51+
public string Title { get; set; } = null!;
52+
53+
[Column("first_aired"), DataType(DataType.Date)]
54+
public DateTime FirstAired { get; set; }
55+
56+
[Column("last_aired"), DataType(DataType.Date)]
57+
public DateTime LastAired { get; set; }
58+
}
59+
60+
[Table("episodes")]
61+
public sealed class Episode
62+
{
63+
[PrimaryKey, Column("series_id")]
64+
public ulong SeriesId { get; set; }
65+
66+
[PrimaryKey, Column("season_id")]
67+
public ulong SeasonId { get; set; }
68+
69+
[PrimaryKey, Column("episode_id")]
70+
public ulong EpisodeId { get; set; }
71+
72+
[Column("title"), NotNull]
73+
public string Title { get; set; } = null!;
74+
75+
[Column("air_date"), DataType(DataType.Date)]
76+
public DateTime AirDate { get; set; }
77+
}
78+
79+
#endregion
80+
81+
#region LINQ2DB DATACONTEXT
82+
83+
internal sealed class MyYdb : DataConnection
84+
{
85+
public MyYdb(string connectionString) : base("YDB", connectionString) { }
86+
public MyYdb(DataOptions options) : base(options) { }
87+
88+
public ITable<Series> Series => this.GetTable<Series>();
89+
public ITable<Season> Seasons => this.GetTable<Season>();
90+
public ITable<Episode> Episodes => this.GetTable<Episode>();
91+
}
92+
93+
#endregion
94+
95+
#region SETTINGS (без CmdOptions)
96+
97+
internal sealed record Settings(
98+
string Host,
99+
int Port,
100+
string Database,
101+
bool UseTls,
102+
int TlsPort)
103+
{
104+
public string SimpleConnectionString =>
105+
$"Host={Host};Port={(UseTls ? TlsPort : Port)};Database={Database};UseTls={(UseTls ? "true" : "false")}";
106+
}
107+
108+
internal static class SettingsLoader
109+
{
110+
public static Settings Load()
111+
{
112+
string host = Environment.GetEnvironmentVariable("YDB_HOST") ?? "localhost";
113+
int port = TryInt(Environment.GetEnvironmentVariable("YDB_PORT"), 2136);
114+
string db = Environment.GetEnvironmentVariable("YDB_DB") ?? "/local";
115+
bool useTls = TryBool(Environment.GetEnvironmentVariable("YDB_USE_TLS"), false);
116+
int tls = TryInt(Environment.GetEnvironmentVariable("YDB_TLS_PORT"), 2135);
117+
118+
return new Settings(host, port, db, useTls, tls);
119+
120+
static int TryInt(string? s, int d) => int.TryParse(s, out var v) ? v : d;
121+
static bool TryBool(string? s, bool d) => bool.TryParse(s, out var v) ? v : d;
122+
}
123+
}
124+
125+
#endregion
126+
127+
internal class AppContext
128+
{
129+
private readonly ILogger<AppContext> _logger;
130+
private readonly Settings _settings;
131+
132+
public AppContext(ILogger<AppContext> logger)
133+
{
134+
_logger = logger;
135+
_settings = SettingsLoader.Load();
136+
}
137+
138+
DataOptions BuildOptions(string? overrideConnectionString = null)
139+
{
140+
var cs = overrideConnectionString ?? _settings.SimpleConnectionString;
141+
return new DataOptions().UseConnectionString("YDB", cs);
142+
}
143+
144+
public async Task Run()
145+
{
146+
_logger.LogInformation("Start app example");
147+
148+
await InitTables();
149+
await LoadData();
150+
await SelectWithParameters();
151+
await RetryPolicy();
152+
153+
154+
await InteractiveTransaction();
155+
await TlsConnectionExample();
156+
await ConnectionWithLoggerFactory();
157+
158+
_logger.LogInformation("Finish app example");
159+
}
160+
161+
private async Task InitTables()
162+
{
163+
DataConnection.AddProviderDetector(YdbTools.ProviderDetector);
164+
await using var db = new MyYdb(BuildOptions());
165+
try { await db.CreateTableAsync<Series>(); } catch { _logger.LogDebug("series exists"); }
166+
try { await db.CreateTableAsync<Season>(); } catch { _logger.LogDebug("seasons exists"); }
167+
try { await db.CreateTableAsync<Episode>(); } catch { _logger.LogDebug("episodes exists"); }
168+
169+
_logger.LogInformation("Created tables");
170+
}
171+
172+
private async Task LoadData()
173+
{
174+
await using var db = new MyYdb(BuildOptions());
175+
176+
var series = new[]
177+
{
178+
new Series { SeriesId = 1, Title = "IT Crowd", ReleaseDate = new DateTime(2006,02,03), SeriesInfo="British sitcom..." },
179+
new Series { SeriesId = 2, Title = "Silicon Valley", ReleaseDate = new DateTime(2014,04,06), SeriesInfo="American comedy..." }
180+
};
181+
foreach (var s in series) await db.InsertAsync(s);
182+
183+
var seasons = new List<Season>
184+
{
185+
new() { SeriesId=1, SeasonId=1, Title="Season 1", FirstAired=new DateTime(2006,02,03), LastAired=new DateTime(2006,03,03)},
186+
new() { SeriesId=1, SeasonId=2, Title="Season 2", FirstAired=new DateTime(2007,08,24), LastAired=new DateTime(2007,09,28)},
187+
new() { SeriesId=1, SeasonId=3, Title="Season 3", FirstAired=new DateTime(2008,11,21), LastAired=new DateTime(2008,12,26)},
188+
new() { SeriesId=1, SeasonId=4, Title="Season 4", FirstAired=new DateTime(2010,06,25), LastAired=new DateTime(2010,07,30)},
189+
new() { SeriesId=2, SeasonId=1, Title="Season 1", FirstAired=new DateTime(2014,04,06), LastAired=new DateTime(2014,06,01)},
190+
new() { SeriesId=2, SeasonId=2, Title="Season 2", FirstAired=new DateTime(2015,04,12), LastAired=new DateTime(2015,06,14)},
191+
new() { SeriesId=2, SeasonId=3, Title="Season 3", FirstAired=new DateTime(2016,04,24), LastAired=new DateTime(2016,06,26)},
192+
new() { SeriesId=2, SeasonId=4, Title="Season 4", FirstAired=new DateTime(2017,04,23), LastAired=new DateTime(2017,06,25)},
193+
new() { SeriesId=2, SeasonId=5, Title="Season 5", FirstAired=new DateTime(2018,03,25), LastAired=new DateTime(2018,05,13)},
194+
};
195+
await db.BulkCopyAsync(seasons);
196+
197+
var eps = new List<Episode>
198+
{
199+
new() { SeriesId=1, SeasonId=1, EpisodeId=1, Title="Yesterday's Jam", AirDate=new DateTime(2006,02,03)},
200+
new() { SeriesId=1, SeasonId=1, EpisodeId=2, Title="Calamity Jen", AirDate=new DateTime(2006,02,03)},
201+
new() { SeriesId=1, SeasonId=1, EpisodeId=3, Title="Fifty-Fifty", AirDate=new DateTime(2006,02,10)},
202+
new() { SeriesId=1, SeasonId=1, EpisodeId=4, Title="The Red Door", AirDate=new DateTime(2006,02,17)},
203+
new() { SeriesId=1, SeasonId=2, EpisodeId=1, Title="The Work Outing", AirDate=new DateTime(2007,08,24)},
204+
new() { SeriesId=1, SeasonId=2, EpisodeId=2, Title="Return of the Golden Child", AirDate=new DateTime(2007,08,31)},
205+
new() { SeriesId=1, SeasonId=3, EpisodeId=1, Title="From Hell", AirDate=new DateTime(2008,11,21)},
206+
new() { SeriesId=1, SeasonId=3, EpisodeId=2, Title="Are We Not Men?", AirDate=new DateTime(2008,11,28)},
207+
new() { SeriesId=1, SeasonId=4, EpisodeId=1, Title="Jen The Fredo", AirDate=new DateTime(2010,06,25)},
208+
new() { SeriesId=1, SeasonId=4, EpisodeId=2, Title="The Final Countdown", AirDate=new DateTime(2010,07,02)},
209+
new() { SeriesId=2, SeasonId=2, EpisodeId=1, Title="Minimum Viable Product", AirDate=new DateTime(2014,04,06)},
210+
new() { SeriesId=2, SeasonId=2, EpisodeId=2, Title="The Cap Table", AirDate=new DateTime(2014,04,13)},
211+
new() { SeriesId=2, SeasonId=1, EpisodeId=3, Title="Articles of Incorporation", AirDate=new DateTime(2014,04,20)},
212+
new() { SeriesId=2, SeasonId=1, EpisodeId=4, Title="Fiduciary Duties", AirDate=new DateTime(2014,04,27)},
213+
};
214+
215+
await db.BulkCopyAsync(eps);
216+
217+
_logger.LogInformation("Loaded data");
218+
}
219+
220+
private async Task SelectWithParameters()
221+
{
222+
await using var db = new MyYdb(BuildOptions());
223+
224+
ulong seriesId = 1;
225+
ulong seasonId = 1;
226+
ulong limit = 3;
227+
228+
var rows = await db.Episodes
229+
.Where(e => e.SeriesId == seriesId && e.SeasonId > seasonId)
230+
.OrderBy(e => e.SeriesId)
231+
.ThenBy(e => e.SeasonId)
232+
.ThenBy(e => e.EpisodeId)
233+
.Take((int)limit)
234+
.Select(e => new { e.SeriesId, e.SeasonId, e.EpisodeId, e.AirDate, e.Title })
235+
.ToListAsync();
236+
237+
_logger.LogInformation("Selected rows:");
238+
foreach (var r in rows)
239+
_logger.LogInformation(
240+
"series_id: {series_id}, season_id: {season_id}, episode_id: {episode_id}, air_date: {air_date}, title: {title}",
241+
r.SeriesId, r.SeasonId, r.EpisodeId, r.AirDate, r.Title);
242+
}
243+
244+
private async Task RetryPolicy()
245+
{
246+
var policy = Policy
247+
.Handle<Exception>(_ => true)
248+
.WaitAndRetryAsync(10, _ => TimeSpan.FromSeconds(1));
249+
250+
await policy.ExecuteAsync(async () =>
251+
{
252+
await using var db = new MyYdb(BuildOptions());
253+
254+
var statsRaw = await db.Episodes
255+
.GroupBy(e => new { e.SeriesId, e.SeasonId })
256+
.Select(g => new
257+
{
258+
SeriesId = g.Key.SeriesId,
259+
SeasonId = g.Key.SeasonId,
260+
Cnt = g.Count()
261+
})
262+
.ToListAsync();
263+
264+
var stats = statsRaw
265+
.OrderBy(x => x.SeriesId)
266+
.ThenBy(x => x.SeasonId);
267+
268+
foreach (var x in stats)
269+
_logger.LogInformation("series_id: {series_id}, season_id: {season_id}, cnt: {cnt}",
270+
x.SeriesId, x.SeasonId, x.Cnt);
271+
});
272+
}
273+
274+
private async Task InteractiveTransaction()
275+
{
276+
await using var db = new MyYdb(BuildOptions());
277+
using var tr = await db.BeginTransactionAsync();
278+
279+
await db.InsertAsync(new Episode
280+
{
281+
SeriesId = 2, SeasonId = 5, EpisodeId = 13,
282+
Title = "Test Episode", AirDate = new DateTime(2018, 08, 27)
283+
});
284+
await db.InsertAsync(new Episode
285+
{
286+
SeriesId = 2, SeasonId = 5, EpisodeId = 21,
287+
Title = "Test 21", AirDate = new DateTime(2018, 08, 27)
288+
});
289+
await db.InsertAsync(new Episode
290+
{
291+
SeriesId = 2, SeasonId = 5, EpisodeId = 22,
292+
Title = "Test 22", AirDate = new DateTime(2018, 08, 27)
293+
});
294+
295+
await tr.CommitAsync();
296+
_logger.LogInformation("Commit transaction");
297+
298+
string title21 = await db.Episodes
299+
.Where(e => e.SeriesId == 2 && e.SeasonId == 5 && e.EpisodeId == 21)
300+
.Select(e => e.Title)
301+
.SingleAsync();
302+
_logger.LogInformation("New episode title: {title}", title21);
303+
304+
string title22 = await db.Episodes
305+
.Where(e => e.SeriesId == 2 && e.SeasonId == 5 && e.EpisodeId == 22)
306+
.Select(e => e.Title)
307+
.SingleAsync();
308+
_logger.LogInformation("New episode title: {title}", title22);
309+
310+
string title13 = await db.Episodes
311+
.Where(e => e.SeriesId == 2 && e.SeasonId == 5 && e.EpisodeId == 13)
312+
.Select(e => e.Title)
313+
.SingleAsync();
314+
_logger.LogInformation("Updated episode title: {title}", title13);
315+
}
316+
317+
private async Task TlsConnectionExample()
318+
{
319+
if (!_settings.UseTls)
320+
{
321+
_logger.LogInformation("Tls example was ignored");
322+
return;
323+
}
324+
325+
var caPath = Path.Combine(
326+
Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), "ca.pem");
327+
var tlsCs = $"Host={_settings.Host};Port={_settings.TlsPort};RootCertificate={caPath}";
328+
await using var db = new MyYdb(BuildOptions(tlsCs));
329+
330+
var rows = await db.Seasons
331+
.Where(sa => sa.SeriesId == 1)
332+
.Join(db.Series, sa => sa.SeriesId, sr => sr.SeriesId,
333+
(sa, sr) => new { SeasonTitle = sa.Title, SeriesTitle = sr.Title, sr.SeriesId, sa.SeasonId })
334+
.OrderBy(x => x.SeriesId).ThenBy(x => x.SeasonId)
335+
.ToListAsync();
336+
337+
foreach (var r in rows)
338+
_logger.LogInformation(
339+
"season_title: {SeasonTitle}, series_title: {SeriesTitle}, series_id: {SeriesId}, season_id: {SeasonId}",
340+
r.SeasonTitle, r.SeriesTitle, r.SeriesId, r.SeasonId);
341+
}
342+
343+
private async Task ConnectionWithLoggerFactory()
344+
{
345+
await using var db = new MyYdb(BuildOptions(
346+
$"Host={_settings.Host};Port={_settings.Port}"));
347+
348+
db.OnTraceConnection = ti =>
349+
{
350+
switch (ti.TraceInfoStep)
351+
{
352+
case TraceInfoStep.BeforeExecute:
353+
_logger.LogInformation("BeforeExecute: {sql}", ti.SqlText);
354+
break;
355+
case TraceInfoStep.AfterExecute:
356+
_logger.LogInformation("AfterExecute: {time} {records} recs", ti.ExecutionTime, ti.RecordsAffected);
357+
break;
358+
case TraceInfoStep.Error:
359+
_logger.LogError(ti.Exception, "SQL error");
360+
break;
361+
}
362+
};
363+
364+
_logger.LogInformation("Dropping tables of examples");
365+
try { await db.DropTableAsync<Episode>(); } catch { /* ignore */ }
366+
try { await db.DropTableAsync<Season>(); } catch { /* ignore */ }
367+
try { await db.DropTableAsync<Series>(); } catch { /* ignore */ }
368+
_logger.LogInformation("Dropped tables of examples");
369+
}
370+
}

0 commit comments

Comments
 (0)