Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions examples/YdbExamples.sln
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Database.Operations.Tutoria
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ydb.Sdk.AdoNet.Yandex.Cloud.Serverless.Container", "Ydb.Sdk.AdoNet.Yandex.Cloud.Serverless.Container\Ydb.Sdk.AdoNet.Yandex.Cloud.Serverless.Container.csproj", "{77625697-498B-4879-BABA-046EE93E7AF7}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "linq2db.Ydb.QuickStart", "linq2db.Ydb.QuickStart\linq2db.Ydb.QuickStart.csproj", "{FCB99CC4-F97D-4BDB-AB5F-C74B40F3CE6E}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -81,6 +83,10 @@ Global
{77625697-498B-4879-BABA-046EE93E7AF7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{77625697-498B-4879-BABA-046EE93E7AF7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{77625697-498B-4879-BABA-046EE93E7AF7}.Release|Any CPU.Build.0 = Release|Any CPU
{FCB99CC4-F97D-4BDB-AB5F-C74B40F3CE6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{FCB99CC4-F97D-4BDB-AB5F-C74B40F3CE6E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FCB99CC4-F97D-4BDB-AB5F-C74B40F3CE6E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FCB99CC4-F97D-4BDB-AB5F-C74B40F3CE6E}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
360 changes: 360 additions & 0 deletions examples/linq2db.Ydb.QuickStart/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,360 @@
using Microsoft.Extensions.Logging;
using Polly;
using LinqToDB;

Check failure on line 3 in examples/linq2db.Ydb.QuickStart/Program.cs

View workflow job for this annotation

GitHub Actions / Analyze (csharp)

The type or namespace name 'LinqToDB' could not be found (are you missing a using directive or an assembly reference?)
using LinqToDB.Async;

Check failure on line 4 in examples/linq2db.Ydb.QuickStart/Program.cs

View workflow job for this annotation

GitHub Actions / Analyze (csharp)

The type or namespace name 'LinqToDB' could not be found (are you missing a using directive or an assembly reference?)
using LinqToDB.Data;

Check failure on line 5 in examples/linq2db.Ydb.QuickStart/Program.cs

View workflow job for this annotation

GitHub Actions / Analyze (csharp)

The type or namespace name 'LinqToDB' could not be found (are you missing a using directive or an assembly reference?)
using LinqToDB.Mapping;

Check failure on line 6 in examples/linq2db.Ydb.QuickStart/Program.cs

View workflow job for this annotation

GitHub Actions / Analyze (csharp)

The type or namespace name 'LinqToDB' could not be found (are you missing a using directive or an assembly reference?)

using var factory = LoggerFactory.Create(b => b.AddConsole());
await new AppContext(factory.CreateLogger<AppContext>()).Run();

#region LINQ2DB MODELS

[Table("series")]

Check failure on line 13 in examples/linq2db.Ydb.QuickStart/Program.cs

View workflow job for this annotation

GitHub Actions / Analyze (csharp)

The type or namespace name 'Table' could not be found (are you missing a using directive or an assembly reference?)

Check failure on line 13 in examples/linq2db.Ydb.QuickStart/Program.cs

View workflow job for this annotation

GitHub Actions / Analyze (csharp)

The type or namespace name 'TableAttribute' could not be found (are you missing a using directive or an assembly reference?)
public sealed class Series
{
[PrimaryKey, Column("series_id")]
public ulong SeriesId { get; set; }

[Column("title"), NotNull]
public string Title { get; set; } = null!;

[Column("series_info")]
public string? SeriesInfo { get; set; }

[Column("release_date"), DataType(DataType.Date)]
public DateTime ReleaseDate { get; set; }
}

[Table("seasons")]

Check failure on line 29 in examples/linq2db.Ydb.QuickStart/Program.cs

View workflow job for this annotation

GitHub Actions / Analyze (csharp)

The type or namespace name 'Table' could not be found (are you missing a using directive or an assembly reference?)

Check failure on line 29 in examples/linq2db.Ydb.QuickStart/Program.cs

View workflow job for this annotation

GitHub Actions / Analyze (csharp)

The type or namespace name 'TableAttribute' could not be found (are you missing a using directive or an assembly reference?)
public sealed class Season
{
[PrimaryKey, Column("series_id")]
public ulong SeriesId { get; set; }

[PrimaryKey, Column("season_id")]
public ulong SeasonId { get; set; }

[Column("title"), NotNull]
public string Title { get; set; } = null!;

[Column("first_aired"), DataType(DataType.Date)]
public DateTime FirstAired { get; set; }

[Column("last_aired"), DataType(DataType.Date)]
public DateTime LastAired { get; set; }
}

[Table("episodes")]
public sealed class Episode
{
[PrimaryKey, Column("series_id")]
public ulong SeriesId { get; set; }

[PrimaryKey, Column("season_id")]
public ulong SeasonId { get; set; }

[PrimaryKey, Column("episode_id")]
public ulong EpisodeId { get; set; }

[Column("title"), NotNull]
public string Title { get; set; } = null!;

[Column("air_date"), DataType(DataType.Date)]
public DateTime AirDate { get; set; }
}

#endregion

#region LINQ2DB DATACONTEXT

internal sealed class MyYdb : DataConnection

Check failure on line 71 in examples/linq2db.Ydb.QuickStart/Program.cs

View workflow job for this annotation

GitHub Actions / Analyze (csharp)

The type or namespace name 'DataConnection' could not be found (are you missing a using directive or an assembly reference?)
{
public MyYdb(string connectionString) : base("YDB", connectionString) {}
public MyYdb(DataOptions options) : base(options) {}

Check failure on line 74 in examples/linq2db.Ydb.QuickStart/Program.cs

View workflow job for this annotation

GitHub Actions / Analyze (csharp)

The type or namespace name 'DataOptions' could not be found (are you missing a using directive or an assembly reference?)

public ITable<Series> Series => this.GetTable<Series>();
public ITable<Season> Seasons => this.GetTable<Season>();
public ITable<Episode> Episodes => this.GetTable<Episode>();
}

#endregion

#region SETTINGS (без CmdOptions)

internal sealed record Settings(
string Host,
int Port,
string Database,
bool UseTls,
int TlsPort)
{
public string SimpleConnectionString =>
$"Host={Host};Port={(UseTls ? TlsPort : Port)};Database={Database};UseTls={(UseTls ? "true" : "false")}";
}

internal static class SettingsLoader
{
public static Settings Load()
{
string host = Environment.GetEnvironmentVariable("YDB_HOST") ?? "localhost";
int port = TryInt(Environment.GetEnvironmentVariable("YDB_PORT"), 2136);
string db = Environment.GetEnvironmentVariable("YDB_DB") ?? "/local";
bool useTls = TryBool(Environment.GetEnvironmentVariable("YDB_USE_TLS"), false);
int tls = TryInt(Environment.GetEnvironmentVariable("YDB_TLS_PORT"), 2135);

return new Settings(host, port, db, useTls, tls);

static int TryInt(string? s, int d) => int.TryParse(s, out var v) ? v : d;
static bool TryBool(string? s, bool d)=> bool.TryParse(s, out var v) ? v : d;
}
}

#endregion

internal class AppContext
{
private readonly ILogger<AppContext> _logger;
private readonly Settings _settings;

public AppContext(ILogger<AppContext> logger)
{
_logger = logger;
_settings = SettingsLoader.Load();
}

DataOptions BuildOptions(string? overrideConnectionString = null)
{
var cs = overrideConnectionString ?? _settings.SimpleConnectionString;
return new DataOptions().UseConnectionString("YDB", cs);
}

public async Task Run()
{
_logger.LogInformation("Start app example");

await InitTables();
await LoadData();
await SelectWithParameters();
await RetryPolicy();

_logger.LogInformation("Clearing all pools...");
_logger.LogInformation("Cleared all pools");

await InteractiveTransaction();
await TlsConnectionExample();
await ConnectionWithLoggerFactory();

_logger.LogInformation("Finish app example");
}


private async Task InitTables()
{
await using var db = new MyYdb(BuildOptions());

try { await db.CreateTableAsync<Series>(); } catch { _logger.LogDebug("series exists"); }
try { await db.CreateTableAsync<Season>(); } catch { _logger.LogDebug("seasons exists"); }
try { await db.CreateTableAsync<Episode>(); } catch { _logger.LogDebug("episodes exists"); }

_logger.LogInformation("Created tables");
}

private async Task LoadData()
{
await using var db = new MyYdb(BuildOptions());

var series = new[]
{
new Series { SeriesId = 1, Title = "IT Crowd", ReleaseDate = new DateTime(2006,02,03), SeriesInfo="British sitcom..." },
new Series { SeriesId = 2, Title = "Silicon Valley",ReleaseDate = new DateTime(2014,04,06), SeriesInfo="American comedy..." }
};
foreach (var s in series) await db.InsertAsync(s);

var seasons = new List<Season>
{
new() { SeriesId=1, SeasonId=1, Title="Season 1", FirstAired=new DateTime(2006,02,03), LastAired=new DateTime(2006,03,03)},
new() { SeriesId=1, SeasonId=2, Title="Season 2", FirstAired=new DateTime(2007,08,24), LastAired=new DateTime(2007,09,28)},
new() { SeriesId=1, SeasonId=3, Title="Season 3", FirstAired=new DateTime(2008,11,21), LastAired=new DateTime(2008,12,26)},
new() { SeriesId=1, SeasonId=4, Title="Season 4", FirstAired=new DateTime(2010,06,25), LastAired=new DateTime(2010,07,30)},
new() { SeriesId=2, SeasonId=1, Title="Season 1", FirstAired=new DateTime(2014,04,06), LastAired=new DateTime(2014,06,01)},
new() { SeriesId=2, SeasonId=2, Title="Season 2", FirstAired=new DateTime(2015,04,12), LastAired=new DateTime(2015,06,14)},
new() { SeriesId=2, SeasonId=3, Title="Season 3", FirstAired=new DateTime(2016,04,24), LastAired=new DateTime(2016,06,26)},
new() { SeriesId=2, SeasonId=4, Title="Season 4", FirstAired=new DateTime(2017,04,23), LastAired=new DateTime(2017,06,25)},
new() { SeriesId=2, SeasonId=5, Title="Season 5", FirstAired=new DateTime(2018,03,25), LastAired=new DateTime(2018,05,13)},
};
await db.BulkCopyAsync(seasons);

var eps = new List<Episode>
{
new() { SeriesId=1, SeasonId=1, EpisodeId=1, Title="Yesterday's Jam", AirDate=new DateTime(2006,02,03)},
new() { SeriesId=1, SeasonId=1, EpisodeId=2, Title="Calamity Jen", AirDate=new DateTime(2006,02,03)},
new() { SeriesId=1, SeasonId=1, EpisodeId=3, Title="Fifty-Fifty", AirDate=new DateTime(2006,02,10)},
new() { SeriesId=1, SeasonId=1, EpisodeId=4, Title="The Red Door", AirDate=new DateTime(2006,02,17)},
new() { SeriesId=1, SeasonId=2, EpisodeId=1, Title="The Work Outing", AirDate=new DateTime(2007,08,24)},
new() { SeriesId=1, SeasonId=2, EpisodeId=2, Title="Return of the Golden Child", AirDate=new DateTime(2007,08,31)},
new() { SeriesId=1, SeasonId=3, EpisodeId=1, Title="From Hell", AirDate=new DateTime(2008,11,21)},
new() { SeriesId=1, SeasonId=3, EpisodeId=2, Title="Are We Not Men?", AirDate=new DateTime(2008,11,28)},
new() { SeriesId=1, SeasonId=4, EpisodeId=1, Title="Jen The Fredo", AirDate=new DateTime(2010,06,25)},
new() { SeriesId=1, SeasonId=4, EpisodeId=2, Title="The Final Countdown", AirDate=new DateTime(2010,07,02)},
new() { SeriesId=2, SeasonId=2, EpisodeId=1, Title="Minimum Viable Product", AirDate=new DateTime(2014,04,06)},
new() { SeriesId=2, SeasonId=2, EpisodeId=2, Title="The Cap Table", AirDate=new DateTime(2014,04,13)},
new() { SeriesId=2, SeasonId=1, EpisodeId=3, Title="Articles of Incorporation", AirDate=new DateTime(2014,04,20)},
new() { SeriesId=2, SeasonId=1, EpisodeId=4, Title="Fiduciary Duties", AirDate=new DateTime(2014,04,27)},
};

await db.BulkCopyAsync(eps);

_logger.LogInformation("Loaded data");
}

private async Task SelectWithParameters()
{
await using var db = new MyYdb(BuildOptions());

ulong seriesId = 1;
ulong seasonId = 1;
ulong limit = 3;

var rows = await db.Episodes
.Where(e => e.SeriesId == seriesId && e.SeasonId > seasonId)
.OrderBy(e => e.SeriesId)
.ThenBy(e => e.SeasonId)
.ThenBy(e => e.EpisodeId)
.Take((int)limit)
.Select(e => new { e.SeriesId, e.SeasonId, e.EpisodeId, e.AirDate, e.Title })
.ToListAsync();

_logger.LogInformation("Selected rows:");
foreach (var r in rows)
_logger.LogInformation(
"series_id: {series_id}, season_id: {season_id}, episode_id: {episode_id}, air_date: {air_date}, title: {title}",
r.SeriesId, r.SeasonId, r.EpisodeId, r.AirDate, r.Title);
}

private async Task RetryPolicy()
{
var policy = Polly.Policy
.Handle<Exception>(_ => true)
.WaitAndRetryAsync(10, _ => TimeSpan.FromSeconds(1));

await policy.ExecuteAsync(async () =>
{
await using var db = new MyYdb(BuildOptions());

var statsRaw = await db.Episodes
.GroupBy(e => new { e.SeriesId, e.SeasonId })
.Select(g => new
{
SeriesId = g.Key.SeriesId,
SeasonId = g.Key.SeasonId,
Cnt = g.Count()
})
.ToListAsync();

var stats = statsRaw
.OrderBy(x => x.SeriesId)
.ThenBy(x => x.SeasonId);

foreach (var x in stats)
_logger.LogInformation("series_id: {series_id}, season_id: {season_id}, cnt: {cnt}",
x.SeriesId, x.SeasonId, x.Cnt);
});
}

private async Task InteractiveTransaction()
{
await using var db = new MyYdb(BuildOptions());
using var tr = await db.BeginTransactionAsync();

await db.InsertAsync(new Episode
{
SeriesId = 2, SeasonId = 5, EpisodeId = 13,
Title = "Test Episode", AirDate = new DateTime(2018, 08, 27)
});
await db.InsertAsync(new Episode
{
SeriesId = 2, SeasonId = 5, EpisodeId = 21,
Title = "Test 21", AirDate = new DateTime(2018, 08, 27)
});
await db.InsertAsync(new Episode
{
SeriesId = 2, SeasonId = 5, EpisodeId = 22,
Title = "Test 22", AirDate = new DateTime(2018, 08, 27)
});

await tr.CommitAsync();
_logger.LogInformation("Commit transaction");

string title21 = await db.Episodes
.Where(e => e.SeriesId == 2 && e.SeasonId == 5 && e.EpisodeId == 21)
.Select(e => e.Title)
.SingleAsync();
_logger.LogInformation("New episode title: {title}", title21);

string title22 = await db.Episodes
.Where(e => e.SeriesId == 2 && e.SeasonId == 5 && e.EpisodeId == 22)
.Select(e => e.Title)
.SingleAsync();
_logger.LogInformation("New episode title: {title}", title22);

string title13 = await db.Episodes
.Where(e => e.SeriesId == 2 && e.SeasonId == 5 && e.EpisodeId == 13)
.Select(e => e.Title)
.SingleAsync();
_logger.LogInformation("Updated episode title: {title}", title13);
}

private async Task TlsConnectionExample()
{
if (!_settings.UseTls)
{
_logger.LogInformation("Tls example was ignored");
return;
}

var caPath = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), "ca.pem");
var tlsCs = $"Host={_settings.Host};Port={_settings.TlsPort};RootCertificate={caPath}";
await using var db = new MyYdb(BuildOptions(tlsCs));

var rows = await db.Seasons
.Where(sa => sa.SeriesId == 1)
.Join(db.Series, sa => sa.SeriesId, sr => sr.SeriesId,
(sa, sr) => new { SeasonTitle = sa.Title, SeriesTitle = sr.Title, sr.SeriesId, sa.SeasonId })
.OrderBy(x => x.SeriesId).ThenBy(x => x.SeasonId)
.ToListAsync();

foreach (var r in rows)
_logger.LogInformation("season_title: {SeasonTitle}, series_title: {SeriesTitle}, series_id: {SeriesId}, season_id: {SeasonId}",
r.SeasonTitle, r.SeriesTitle, r.SeriesId, r.SeasonId);
}

private async Task ConnectionWithLoggerFactory()
{
await using var db = new MyYdb(BuildOptions(
$"Host={_settings.Host};Port={_settings.Port}"));

db.OnTraceConnection = ti =>
{
switch (ti.TraceInfoStep)
{
case TraceInfoStep.BeforeExecute:
_logger.LogInformation("BeforeExecute: {sql}", ti.SqlText);
break;
case TraceInfoStep.AfterExecute:
_logger.LogInformation("AfterExecute: {time} {records} recs", ti.ExecutionTime, ti.RecordsAffected);
break;
case TraceInfoStep.Error:
_logger.LogError(ti.Exception, "SQL error");
break;
}
};

_logger.LogInformation("Dropping tables of examples");
try { await db.DropTableAsync<Episode>(); } catch { /* ignore */ }
try { await db.DropTableAsync<Season>(); } catch { /* ignore */ }
try { await db.DropTableAsync<Series>(); } catch { /* ignore */ }
_logger.LogInformation("Dropped tables of examples");
}
}
18 changes: 18 additions & 0 deletions examples/linq2db.Ydb.QuickStart/linq2db.Ydb.QuickStart.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\..\..\linq2db\Source\LinqToDB\LinqToDB.csproj" />
<PackageReference Include="CommandLineParser" Version="2.8.0"/>
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="5.0.0"/>
<PackageReference Include="Microsoft.Extensions.Logging" Version="5.0.0"/>
<PackageReference Include="Polly" Version="8.4.1" />
</ItemGroup>

</Project>
Loading