diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 210b8249..637eeeb7 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -11,14 +11,14 @@ jobs: strategy: fail-fast: false matrix: - source-dir: ["./src/", "./examples/src/", "./slo/src/"] + source-dir: ["./src/", "./examples/", "./slo/src/"] include: - source-dir: "./src/" solutionFile: "YdbSdk.sln" - - source-dir: "./examples/src/" + - source-dir: "./examples/" solutionFile: "YdbExamples.sln" - source-dir: "./slo/src/" - solutionFile: "src.sln" + solutionFile: "src.sln" name: autoformat check runs-on: ubuntu-latest steps: @@ -41,7 +41,7 @@ jobs: strategy: fail-fast: false matrix: - solutionPath: ["./src/YdbSdk.sln", "./examples/src/YdbExamples.sln", "./slo/src/src.sln"] + solutionPath: ["./src/YdbSdk.sln", "./examples/YdbExamples.sln", "./slo/src/src.sln"] runs-on: ubuntu-latest name: Inspection steps: diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index b71f4a75..e0e437c0 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -182,24 +182,32 @@ jobs: uses: actions/setup-dotnet@v4 with: dotnet-version: ${{ matrix.dotnet-version }} - - name: Run ADO.NET example + - name: Run Ydb.Sdk.AdoNet.QuickStart run: | docker cp ydb-local:/ydb_certs/ca.pem ~/ - cd ./examples/src/AdoNet + cd ./examples/Ydb.Sdk.AdoNet.QuickStart dotnet run - - name: Run Dapper example + - name: Run Ydb.Sdk.AdoNet.Dapper.QuickStart run: | - cd ./examples/src/DapperExample + cd ./examples/Ydb.Sdk.AdoNet.Dapper.QuickStart dotnet run - - name: Run YDB Topic example + - name: Run Ydb.Sdk.Topic.QuickStart run: | - cd ./examples/src/Topic + cd ./examples/Ydb.Sdk.Topic.QuickStart dotnet run - - name: Run Entity Framework Core example + - name: Run EntityFrameworkCore.Ydb.QuickStart run: | - cd ./examples/src/EntityFrameworkCore.Ydb.QuickStart + cd ./examples/EntityFrameworkCore.Ydb.QuickStart dotnet tool install --global dotnet-ef dotnet add package Microsoft.EntityFrameworkCore.Design dotnet ef migrations add InitialCreate dotnet ef database update dotnet run + - name: Run EntityFrameworkCore.Ydb.Samples/Database.Operations.Tutorial + run: | + cd ./examples/EntityFrameworkCore.Ydb.Samples/Database.Operations.Tutorial + dotnet tool install --global dotnet-ef + dotnet add package Microsoft.EntityFrameworkCore.Design + dotnet ef migrations add InitialCreate + dotnet ef database update + dotnet run \ No newline at end of file diff --git a/examples/src/EntityFrameworkCore.Ydb.QuickStart/EntityFrameworkCore.Ydb.QuickStart.csproj b/examples/EntityFrameworkCore.Ydb.QuickStart/EntityFrameworkCore.Ydb.QuickStart.csproj similarity index 77% rename from examples/src/EntityFrameworkCore.Ydb.QuickStart/EntityFrameworkCore.Ydb.QuickStart.csproj rename to examples/EntityFrameworkCore.Ydb.QuickStart/EntityFrameworkCore.Ydb.QuickStart.csproj index b20b9a8f..0622e0c7 100644 --- a/examples/src/EntityFrameworkCore.Ydb.QuickStart/EntityFrameworkCore.Ydb.QuickStart.csproj +++ b/examples/EntityFrameworkCore.Ydb.QuickStart/EntityFrameworkCore.Ydb.QuickStart.csproj @@ -5,11 +5,11 @@ net8.0 enable enable - EntityFrameworkCore.Ydb + EntityFrameworkCore.Ydb.QuickStart - + diff --git a/examples/src/EntityFrameworkCore.Ydb.QuickStart/Program.cs b/examples/EntityFrameworkCore.Ydb.QuickStart/Program.cs similarity index 100% rename from examples/src/EntityFrameworkCore.Ydb.QuickStart/Program.cs rename to examples/EntityFrameworkCore.Ydb.QuickStart/Program.cs diff --git a/examples/src/EntityFrameworkCore.Ydb.QuickStart/README.md b/examples/EntityFrameworkCore.Ydb.QuickStart/README.md similarity index 76% rename from examples/src/EntityFrameworkCore.Ydb.QuickStart/README.md rename to examples/EntityFrameworkCore.Ydb.QuickStart/README.md index 0d72ab69..86b56ec9 100644 --- a/examples/src/EntityFrameworkCore.Ydb.QuickStart/README.md +++ b/examples/EntityFrameworkCore.Ydb.QuickStart/README.md @@ -1,11 +1,13 @@ # Entity Framework Core YDB Quick Start -A sample application from the [official documentation](https://learn.microsoft.com/en-us/ef/core/get-started/overview/first-app?tabs=netcore-cli) +A sample application from +the [official documentation](https://learn.microsoft.com/en-us/ef/core/get-started/overview/first-app?tabs=netcore-cli) shows how to get started with EF, define a model, populate it with data and then query the database. ## Running QuickStart 1. Setup [YDB local](https://ydb.tech/docs/en/reference/docker/start). + 2. Create the database: ```bash dotnet tool install --global dotnet-ef @@ -13,4 +15,5 @@ shows how to get started with EF, define a model, populate it with data and then dotnet ef migrations add InitialCreate dotnet ef database update ``` + 3. Run the app: `dotnet run` diff --git a/examples/EntityFrameworkCore.Ydb.Samples/AddEntity.Sample/AddEntity.Sample.csproj b/examples/EntityFrameworkCore.Ydb.Samples/AddEntity.Sample/AddEntity.Sample.csproj new file mode 100644 index 00000000..5b482857 --- /dev/null +++ b/examples/EntityFrameworkCore.Ydb.Samples/AddEntity.Sample/AddEntity.Sample.csproj @@ -0,0 +1,21 @@ + + + + Exe + net8.0 + enable + enable + Section_1.HR + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + diff --git a/examples/EntityFrameworkCore.Ydb.Samples/AddEntity.Sample/HRContext.cs b/examples/EntityFrameworkCore.Ydb.Samples/AddEntity.Sample/HRContext.cs new file mode 100644 index 00000000..542a6ebf --- /dev/null +++ b/examples/EntityFrameworkCore.Ydb.Samples/AddEntity.Sample/HRContext.cs @@ -0,0 +1,51 @@ +// using Microsoft.EntityFrameworkCore; + +using EntityFrameworkCore.Ydb.Extensions; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Design; +using Microsoft.EntityFrameworkCore.Diagnostics; +using Microsoft.Extensions.Logging; + +namespace Section_1.HR; + +public class HRContext : DbContext +{ + public DbSet Employees { get; set; } + public DbSet Departments { get; set; } + + public HRContext() + { + } + + public HRContext(DbContextOptions options) : base(options) + { + } + + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) => + optionsBuilder + .UseYdb("Host=localhost;Port=2136;Database=/local") + .LogTo(Console.WriteLine, new[] { DbLoggerCategory.Database.Command.Name }, LogLevel.Information); +} + +internal class HRContextContextFactory : IDesignTimeDbContextFactory +{ + public HRContext CreateDbContext(string[] args) + { + var optionsBuilder = new DbContextOptionsBuilder(); + + // IMPORTANT! + // Disables retries for the migrations context. + // Required because migration operations may use suppressed or explicit transactions, + // and enabling retries in this case leads to runtime errors with this provider. + // + // "System.NotSupportedException: User transaction is not supported with a TransactionSuppressed migrations or a retrying execution strategy." + // + // Bottom line: ALWAYS disable retries for design-time/migration contexts to avoid migration failures and errors. + return new HRContext( + optionsBuilder + .UseYdb("Host=localhost;Port=2136;Database=/local", builder => builder.DisableRetryOnFailure()) + .ConfigureWarnings(warnings => warnings.Ignore(RelationalEventId.PendingModelChangesWarning)) + .Options + ); + } +} \ No newline at end of file diff --git a/examples/EntityFrameworkCore.Ydb.Samples/AddEntity.Sample/Models.cs b/examples/EntityFrameworkCore.Ydb.Samples/AddEntity.Sample/Models.cs new file mode 100644 index 00000000..b3ae7e60 --- /dev/null +++ b/examples/EntityFrameworkCore.Ydb.Samples/AddEntity.Sample/Models.cs @@ -0,0 +1,17 @@ +namespace Section_1.HR; + +public class Department +{ + public int Id { get; set; } + public required string Name { get; set; } +} + +public class Employee +{ + public int Id { get; set; } + public required string FirstName { get; set; } + public required string LastName { get; set; } + public required DateTime JoinedDate { get; set; } + public required decimal Salary { get; set; } + public Department? Department { get; set; } +} \ No newline at end of file diff --git a/examples/EntityFrameworkCore.Ydb.Samples/AddEntity.Sample/Program.cs b/examples/EntityFrameworkCore.Ydb.Samples/AddEntity.Sample/Program.cs new file mode 100644 index 00000000..d820a45a --- /dev/null +++ b/examples/EntityFrameworkCore.Ydb.Samples/AddEntity.Sample/Program.cs @@ -0,0 +1,39 @@ +// See https://aka.ms/new-console-template for more information + +using Section_1.HR; + +InsertDepartments(); +SelectDepartments(); + +return; + + +static void InsertDepartments() +{ + var departments = new List + { + new() { Name = "Sales" }, + new() { Name = "Marketing" }, + new() { Name = "Logistics" } + }; + + using var context = new HRContext(); + + foreach (var department in departments) + { + context.Departments.Add(department); + } + + context.SaveChanges(); +} + +static void SelectDepartments() +{ + using var context = new HRContext(); + var departments = context.Departments.ToList(); + + foreach (var department in departments) + { + Console.WriteLine($"{department.Id} - {department.Name}"); + } +} \ No newline at end of file diff --git a/examples/EntityFrameworkCore.Ydb.Samples/AddEntity.Sample/README.md b/examples/EntityFrameworkCore.Ydb.Samples/AddEntity.Sample/README.md new file mode 100644 index 00000000..4d600f18 --- /dev/null +++ b/examples/EntityFrameworkCore.Ydb.Samples/AddEntity.Sample/README.md @@ -0,0 +1,27 @@ +# Entity Framework Core YDB Add Entity Sample + +This sample introduces the basics of using EF Core with YDB. +It covers what EF Core is, how to create a simple .NET app with EF Core, how to define a data model, set up a database connection, and perform basic CRUD operations via the EF Core API. + +Based on the [tutorial](https://www.csharptutorial.net/entity-framework-core-tutorial/getting-started-with-entity-framework-core/). + +## Running QuickStart + +1. Set up [YDB local](https://ydb.tech/docs/en/reference/docker/start). + +2. Install the EF Core CLI tool and dependencies (if needed): + ```bash + dotnet tool install --global dotnet-ef + dotnet add package Microsoft.EntityFrameworkCore.Design + ``` + +3. Create the database and apply migrations: + ```bash + dotnet ef migrations add InitialCreate + dotnet ef database update + ``` + +4. Run the sample: + ```bash + dotnet run + ``` diff --git a/examples/EntityFrameworkCore.Ydb.Samples/Database.Operations.Tutorial/Database.Operations.Tutorial.csproj b/examples/EntityFrameworkCore.Ydb.Samples/Database.Operations.Tutorial/Database.Operations.Tutorial.csproj new file mode 100644 index 00000000..e9e7ec20 --- /dev/null +++ b/examples/EntityFrameworkCore.Ydb.Samples/Database.Operations.Tutorial/Database.Operations.Tutorial.csproj @@ -0,0 +1,22 @@ + + + + Exe + net8.0 + enable + enable + Database.Operations.Tutorial + + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + diff --git a/examples/EntityFrameworkCore.Ydb.Samples/Database.Operations.Tutorial/Department.cs b/examples/EntityFrameworkCore.Ydb.Samples/Database.Operations.Tutorial/Department.cs new file mode 100644 index 00000000..6d7ec163 --- /dev/null +++ b/examples/EntityFrameworkCore.Ydb.Samples/Database.Operations.Tutorial/Department.cs @@ -0,0 +1,7 @@ +namespace Database.Operations.Tutorial; + +public class Department +{ + public int Id { get; set; } + public required string Name { get; set; } +} \ No newline at end of file diff --git a/examples/EntityFrameworkCore.Ydb.Samples/Database.Operations.Tutorial/Employee.cs b/examples/EntityFrameworkCore.Ydb.Samples/Database.Operations.Tutorial/Employee.cs new file mode 100644 index 00000000..aa36126d --- /dev/null +++ b/examples/EntityFrameworkCore.Ydb.Samples/Database.Operations.Tutorial/Employee.cs @@ -0,0 +1,22 @@ +namespace Database.Operations.Tutorial; + +public class Employee +{ + public int Id { get; set; } + public required string FirstName { get; set; } + public required string LastName { get; set; } + public required decimal Salary { get; set; } + public required DateTime JoinedDate { get; set; } + + // Foreign key property to the Department + public int DepartmentId { get; set; } + + // Reference navigation to Department + public Department Department { get; set; } = null!; + + // Reference navigation to EmployeeProfile + public EmployeeProfile? Profile { get; set; } + + // collection navigation to Employee + public List Skills { get; set; } = new(); +} \ No newline at end of file diff --git a/examples/EntityFrameworkCore.Ydb.Samples/Database.Operations.Tutorial/EmployeeProfile.cs b/examples/EntityFrameworkCore.Ydb.Samples/Database.Operations.Tutorial/EmployeeProfile.cs new file mode 100644 index 00000000..b1b5343b --- /dev/null +++ b/examples/EntityFrameworkCore.Ydb.Samples/Database.Operations.Tutorial/EmployeeProfile.cs @@ -0,0 +1,11 @@ +namespace Database.Operations.Tutorial; + +public class EmployeeProfile +{ + public int Id { get; set; } + public required string Phone { get; set; } + public required string Email { get; set; } + + public int EmployeeId { get; set; } + public Employee Employee { get; set; } = null!; +} \ No newline at end of file diff --git a/examples/EntityFrameworkCore.Ydb.Samples/Database.Operations.Tutorial/HRContext.cs b/examples/EntityFrameworkCore.Ydb.Samples/Database.Operations.Tutorial/HRContext.cs new file mode 100644 index 00000000..404e06e3 --- /dev/null +++ b/examples/EntityFrameworkCore.Ydb.Samples/Database.Operations.Tutorial/HRContext.cs @@ -0,0 +1,38 @@ +using EntityFrameworkCore.Ydb.Extensions; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Diagnostics; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Logging; + +namespace Database.Operations.Tutorial; + +public class HRContext : DbContext +{ + public DbSet Employees { get; set; } + public DbSet Departments { get; set; } + public DbSet EmployeeProfiles { get; set; } + public DbSet Skills { get; set; } + + public HRContext() + { + } + + public HRContext(DbContextOptions options) : base(options) + { + } + + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + { + var configuration = new ConfigurationBuilder() + .SetBasePath(Directory.GetCurrentDirectory()) + .AddJsonFile("appsettings.json") + .Build(); + + var connectionString = configuration.GetConnectionString("Local"); + + optionsBuilder.UseYdb(connectionString) + .LogTo(Console.WriteLine, new[] { DbLoggerCategory.Database.Command.Name }, LogLevel.Information) + .ConfigureWarnings(warnings => warnings.Ignore(RelationalEventId.PendingModelChangesWarning)) + .EnableSensitiveDataLogging(); + } +} \ No newline at end of file diff --git a/examples/EntityFrameworkCore.Ydb.Samples/Database.Operations.Tutorial/HRContextFactory.cs b/examples/EntityFrameworkCore.Ydb.Samples/Database.Operations.Tutorial/HRContextFactory.cs new file mode 100644 index 00000000..aeebe2b5 --- /dev/null +++ b/examples/EntityFrameworkCore.Ydb.Samples/Database.Operations.Tutorial/HRContextFactory.cs @@ -0,0 +1,38 @@ +using EntityFrameworkCore.Ydb.Extensions; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Design; +using Microsoft.EntityFrameworkCore.Diagnostics; +using Microsoft.Extensions.Configuration; + +namespace Database.Operations.Tutorial; + +// ReSharper disable once UnusedType.Global +internal class HRContextFactory : IDesignTimeDbContextFactory +{ + public HRContext CreateDbContext(string[] args) + { + var optionsBuilder = new DbContextOptionsBuilder(); + + var configuration = new ConfigurationBuilder() + .SetBasePath(Directory.GetCurrentDirectory()) + .AddJsonFile("appsettings.json") + .Build(); + + var connectionString = configuration.GetConnectionString("Local"); + + // IMPORTANT! + // Disables retries for the migrations context. + // Required because migration operations may use suppressed or explicit transactions, + // and enabling retries in this case leads to runtime errors with this provider. + // + // "System.NotSupportedException: User transaction is not supported with a TransactionSuppressed migrations or a retrying execution strategy." + // + // Bottom line: ALWAYS disable retries for design-time/migration contexts to avoid migration failures and errors. + return new HRContext( + optionsBuilder + .UseYdb(connectionString, builder => builder.DisableRetryOnFailure()) + .ConfigureWarnings(warnings => warnings.Ignore(RelationalEventId.PendingModelChangesWarning)) + .Options + ); + } +} \ No newline at end of file diff --git a/examples/EntityFrameworkCore.Ydb.Samples/Database.Operations.Tutorial/Program.cs b/examples/EntityFrameworkCore.Ydb.Samples/Database.Operations.Tutorial/Program.cs new file mode 100644 index 00000000..2782d8c2 --- /dev/null +++ b/examples/EntityFrameworkCore.Ydb.Samples/Database.Operations.Tutorial/Program.cs @@ -0,0 +1,538 @@ +using Database.Operations.Tutorial; +using Microsoft.EntityFrameworkCore; + +var orderingActions = new List +{ + Action.ReadCsv, + Action.Select, + Action.OrderBy, + Action.QueryLinq, + Action.Where, + Action.InOperator, + Action.Like, + Action.InnerJoin, + Action.GroupBy +}; +var actions = new Dictionary> +{ + { Action.ReadCsv, ReadCSV }, + { Action.Select, Select }, + { Action.OrderBy, OrderBy }, + { Action.QueryLinq, QueryLinq }, + { Action.Where, Where }, + { Action.InOperator, InOperator }, + { Action.Like, Like }, + { Action.InnerJoin, InnerJoin }, + { Action.GroupBy, GroupBy } +}; + +switch (args.Length) +{ + case > 0: + { + if (Enum.TryParse(args[0], true, out var result)) + { + using var hrContext = new HRContext(); + actions[result](hrContext); + break; + } + + throw new InvalidOperationException("Not found argument: " + args[0]); + } + case 0: + { + foreach (var action in orderingActions) + { + using var context = new HRContext(); + actions[action](context); + } + + break; + } +} + +return; + +static void GroupBy(HRContext context) +{ + { + var groups = context.Employees + .GroupBy(e => e.Department) + .Select(group => new + { + group.Key.Name, + Headcount = group.Count() + }) + .OrderBy(dc => dc.Name) + .ToList(); + + foreach (var group in groups) + { + Console.WriteLine($"{group.Name,-20}{group.Headcount}"); + } + } + { + var groups = context.Employees + .GroupBy(e => e.Department) + .Select(group => new + { + group.Key.Name, + Headcount = group.Count() + }) + .Where(group => group.Headcount > 11) + .OrderBy(dc => dc.Name) + .ToList(); + + foreach (var group in groups) + { + Console.WriteLine($"{group.Name,-20}{group.Headcount}"); + } + } +} + +static void InnerJoin(HRContext context) +{ + { + Console.WriteLine("Get employees & departments"); + + var employees = context.Employees.Include(e => e.Department) + .OrderBy(e => e.FirstName) + .ToList(); + + foreach (var e in employees) + { + Console.WriteLine($"{e.FirstName} {e.LastName} - {e.Department.Name}"); + } + } + { + Console.WriteLine("EF Core Inner Join in a many-to-many relationship"); + var employees = context.Employees.Include(e => e.Skills) + .OrderBy(e => e.FirstName) + .ToList(); + + foreach (var e in employees) + { + Console.WriteLine($"{e.FirstName} {e.LastName}"); + + foreach (var skill in e.Skills) + { + Console.WriteLine($"- {skill.Title}"); + } + } + } + { + Console.WriteLine("Using multiple joins"); + var employees = context.Employees.Include(e => e.Department) + .Include(e => e.Skills) + .OrderBy(e => e.FirstName) + .ToList(); + + foreach (var e in employees) + { + Console.WriteLine($"{e.FirstName} {e.LastName} - {e.Department.Name}"); + + foreach (var skill in e.Skills) + { + Console.WriteLine($"- {skill.Title}"); + } + } + } +} + +static void Like(HRContext context) +{ + { + Console.WriteLine("Using the % wildcard character"); + + var keyword = "%ac%"; + var employees = context.Employees + .Where(e => EF.Functions.Like(e.FirstName, keyword)) + .ToList(); + + foreach (var e in employees) + { + Console.WriteLine($"{e.FirstName} {e.LastName}"); + } + } + { + var keyword = "da%"; + var employees = context.Employees + .Where(e => EF.Functions.Like(e.FirstName, keyword)) + .ToList(); + + foreach (var e in employees) + { + Console.WriteLine($"{e.FirstName} {e.LastName}"); + } + } + { + Console.WriteLine("Using the _ wildcard character"); + + var keyword = "H_n%"; + var employees = context.Employees + .Where(e => EF.Functions.Like(e.FirstName, keyword)) + .ToList(); + + foreach (var e in employees) + { + Console.WriteLine($"{e.FirstName} {e.LastName}"); + } + } +} + +static void InOperator(HRContext context) +{ + { + Console.WriteLine("Introduction to EF Core Where In"); + + int[] ids = [1, 2, 3]; + + var employees = context.Employees + .Where(e => ids.Contains(e.Id)) + .ToList(); + + foreach (var e in employees) + { + Console.WriteLine($"{e.Id} - {e.FirstName} {e.LastName}"); + } + } + + { + Console.WriteLine("NOT IN"); + + int[] ids = [1, 2, 3]; + + var employees = context.Employees + .Where(e => !ids.Contains(e.Id)) + .ToList(); + + foreach (var e in employees) + { + Console.WriteLine($"{e.Id} - {e.FirstName} {e.LastName}"); + } + } +} + +static void Where(HRContext context) +{ + { + Console.WriteLine("Using the Where() method with the equal operator"); + // ReSharper disable once RedundantAssignment + var employees = context.Employees + .Where(e => e.FirstName == "Alexander") + .ToList(); + + // ReSharper disable once ConvertToConstant.Local + var firstName = "Alexander"; + employees = context.Employees + .Where(e => e.FirstName == firstName) + .ToList(); + + foreach (var e in employees) + { + Console.WriteLine($"{e.FirstName} {e.LastName}"); + } + } + { + Console.WriteLine("Using AND operator"); + var firstName = "Alexander"; + var lastName = "Young"; + + var employees = context.Employees + .Where(e => e.FirstName == firstName && e.LastName == lastName) + .ToList(); + + foreach (var e in employees) + { + Console.WriteLine($"{e.FirstName} {e.LastName}"); + } + } + { + var startDate = new DateTime(2023, 3, 1); + var endDate = new DateTime(2023, 3, 31); + + var employees = context.Employees + .Where(e => e.JoinedDate >= startDate && e.JoinedDate <= endDate) + .ToList(); + + foreach (var e in employees) + { + Console.WriteLine($"{e.FirstName} {e.LastName} - {e.JoinedDate.ToShortDateString()}"); + } + } + { + Console.WriteLine("Using OR operator"); + var firstName = "Emily"; + var lastName = "Brown"; + + var employees = context.Employees + .Where(e => e.FirstName == firstName || e.LastName == lastName) + .ToList(); + + foreach (var e in employees) + { + Console.WriteLine($"{e.FirstName} {e.LastName}"); + } + } +} + +static void QueryLinq(HRContext context) +{ + { + Console.WriteLine("EF Core Query flow"); + + var departments = (from d in context.Departments select d).ToList(); + + foreach (var department in departments) + { + Console.WriteLine($"{department.Id}: {department.Name}"); + } + } + { + Console.WriteLine("Query enumeration"); + + foreach (var d in context.Departments) + { + Console.WriteLine($"{d.Id} {d.Name}"); + } + } + { + Console.WriteLine("Filtering data (WHERE Name == 'Sales')"); + var name = "Sales"; + // ReSharper disable once RedundantAssignment + var department = context.Departments + .Where(d => d.Name == "Sales") + .ToList(); + + department = context.Departments + .Where(d => d.Name == name) + .ToList(); + + foreach (var d in department) + { + Console.WriteLine($"{d.Id} {d.Name}"); + } + } + { + Console.WriteLine("Filtering partial texts (LIKE)"); + var keyword = "i"; + var departments = context.Departments + .Where(d => EF.Functions.Like(d.Name, $"%{keyword}%")) + .ToList(); + + foreach (var d in departments) + { + Console.WriteLine($"{d.Id} {d.Name}"); + } + } + { + Console.WriteLine("Finding an entity by its key value"); + var department = context.Departments.Find(1); + + Console.WriteLine($"{department?.Id} {department?.Name}"); + } +} + +static void OrderBy(HRContext context) +{ + Console.WriteLine("Sorting by one column in ascending order"); + + var listByOneColumnInAscendingOrder = context.Employees + .OrderBy(e => e.FirstName) + .Take(7) + .ToList(); + + foreach (var e in listByOneColumnInAscendingOrder) + { + Console.WriteLine(e.FirstName); + } + + Console.WriteLine("Sorting by two or more columns in ascending order"); + + var listByTwoOrMoreColumnsInAscendingOrder = context.Employees + .OrderBy(e => e.FirstName) + .ThenBy(e => e.LastName) + .Take(7) + .ToList(); + + foreach (var e in listByTwoOrMoreColumnsInAscendingOrder) + { + Console.WriteLine($"{e.FirstName} {e.LastName}"); + } + + Console.WriteLine("Sorting by one column in descending order"); + + var listByOneColumnInDescendingOrder = context.Employees + .OrderByDescending(e => e.FirstName) + .Take(7) + .ToList(); + + foreach (var e in listByOneColumnInDescendingOrder) + { + Console.WriteLine(e.FirstName); + } + + Console.WriteLine("Sorting by two or more columns in descending order"); + + var listByTwoOrMoreColumnsInDescendingOrder = context.Employees + .OrderByDescending(e => e.FirstName) + .ThenByDescending(e => e.LastName) + .Take(7) + .ToList(); + + foreach (var e in listByTwoOrMoreColumnsInDescendingOrder) + { + Console.WriteLine(e.FirstName); + } + + Console.WriteLine("Sorting one column in ascending order and another column in descending order"); + + var list = context.Employees + .OrderBy(e => e.JoinedDate) + .ThenByDescending(e => e.FirstName) + .Take(6) + .ToList(); + + foreach (var e in list) + { + Console.WriteLine($"{e.JoinedDate.ToShortDateString()} {e.LastName}"); + } +} + +static void Select(HRContext context) +{ + Console.WriteLine("Selecting all rows from a table"); + + var employees = context.Employees.ToList(); + + foreach (var e in employees) + { + Console.WriteLine($"{e.FirstName} {e.LastName}"); + } + + Console.WriteLine("Selecting some columns from a table"); + + var names = context.Employees + .Select(e => $"{e.FirstName} {e.LastName}") + .ToList(); + + foreach (var name in names) + { + Console.WriteLine(name); + } + + Console.WriteLine("Returning a list of anonymous objects"); + + var list = context.Employees + .Select(e => new { e.FirstName, e.JoinedDate }) + .ToList(); + + foreach (var e in list) + { + Console.WriteLine($"{e.FirstName} - {e.JoinedDate.ToShortDateString()}"); + } +} + +static void ReadCSV(HRContext context) +{ + var employees = new List(); + var skills = new List(); + var departments = new List(); + + using var reader = new StreamReader("data.csv"); + + // Read the header line + _ = reader.ReadLine(); + + while (!reader.EndOfStream) + { + var line = reader.ReadLine(); + if (line == null) + { + break; + } + + // Split the line by comma + var values = line.Split(','); + + if (values.Length == 8) + { + var firstName = values[0]; + var lastName = values[1]; + var salary = values[2]; + var joinedDate = DateTime.ParseExact(values[3], "M/d/yyyy", null); + var phone = values[4]; + var email = values[5]; + var departmentName = values[6]; + var skillTitles = values[7].Split(';'); + + // Create Department object if it doesn't exist + var department = departments.Find(d => d.Name == departmentName); + if (department == null) + { + department = new Department + { + Name = departmentName + }; + departments.Add(department); + } + + // Create EmployeeProfile object + var profile = new EmployeeProfile + { + Phone = phone, + Email = email + }; + + // Create Employee object + var employee = new Employee + { + FirstName = firstName, + LastName = lastName, + Salary = decimal.Parse(salary), + JoinedDate = joinedDate, + Department = department, + Profile = profile + }; + employees.Add(employee); + + // Create Skill objects + foreach (var skillTitle in skillTitles) + { + var skill = skills.Find(s => s.Title == skillTitle); + if (skill == null) + { + skill = new Skill + { + Title = skillTitle + }; + skills.Add(skill); + } + + // Add skill to the employee's Skills collection + employee.Skills.Add(skill); + } + } + } + + Console.WriteLine($"{employees.Count} row(s) found"); + foreach (var employee in employees) + { + context.Add(employee); + } + + context.SaveChanges(); +} + +internal enum Action +{ + ReadCsv, + Select, + OrderBy, + QueryLinq, + Where, + InOperator, + Like, + InnerJoin, + GroupBy +} \ No newline at end of file diff --git a/examples/EntityFrameworkCore.Ydb.Samples/Database.Operations.Tutorial/README.md b/examples/EntityFrameworkCore.Ydb.Samples/Database.Operations.Tutorial/README.md new file mode 100644 index 00000000..45a23410 --- /dev/null +++ b/examples/EntityFrameworkCore.Ydb.Samples/Database.Operations.Tutorial/README.md @@ -0,0 +1,44 @@ +# Querying data using LINQ from Entity Framework Core YDB + +This sample demonstrates how to query data from YDB using Entity Framework Core and LINQ. + +Based on the [tutorial](https://www.csharptutorial.net/entity-framework-core-tutorial/). + +## Running + +1. Set up [YDB local](https://ydb.tech/docs/en/reference/docker/start). + +2. Install the EF Core CLI tool and dependencies (if needed): + ```bash + dotnet tool install --global dotnet-ef + dotnet add package Microsoft.EntityFrameworkCore.Design + ``` + +3. Create the database and apply migrations: + ```bash + dotnet ef migrations add InitialCreate + dotnet ef database update + ``` + +4. Run all operations sequentially with: + ```bash + dotnet run + ``` + +## Available Operations + +You can execute individual operations by specifying the desired action as a command-line argument. Available actions: + +- ReadCsv +- Select +- OrderBy +- QueryLinq +- Where +- InOperator +- Like +- InnerJoin +- GroupBy + +```bash +dotnet run Select +``` \ No newline at end of file diff --git a/examples/EntityFrameworkCore.Ydb.Samples/Database.Operations.Tutorial/Skill.cs b/examples/EntityFrameworkCore.Ydb.Samples/Database.Operations.Tutorial/Skill.cs new file mode 100644 index 00000000..c04e2ed6 --- /dev/null +++ b/examples/EntityFrameworkCore.Ydb.Samples/Database.Operations.Tutorial/Skill.cs @@ -0,0 +1,10 @@ +namespace Database.Operations.Tutorial; + +public class Skill +{ + public int Id { get; set; } + public required string Title { get; set; } + + // collection navigation to Employee + public List Employees { get; set; } = new(); +} \ No newline at end of file diff --git a/examples/EntityFrameworkCore.Ydb.Samples/Database.Operations.Tutorial/appsettings.json b/examples/EntityFrameworkCore.Ydb.Samples/Database.Operations.Tutorial/appsettings.json new file mode 100644 index 00000000..d649f8bb --- /dev/null +++ b/examples/EntityFrameworkCore.Ydb.Samples/Database.Operations.Tutorial/appsettings.json @@ -0,0 +1,5 @@ +{ + "ConnectionStrings": { + "Local": "Host=localhost;Port=2136;Database=/local" + } +} diff --git a/examples/EntityFrameworkCore.Ydb.Samples/Database.Operations.Tutorial/data.csv b/examples/EntityFrameworkCore.Ydb.Samples/Database.Operations.Tutorial/data.csv new file mode 100644 index 00000000..0c04609b --- /dev/null +++ b/examples/EntityFrameworkCore.Ydb.Samples/Database.Operations.Tutorial/data.csv @@ -0,0 +1,61 @@ +First Name,Last Name,Salary,Joined Date,Phone,Email,Department,Skills +John,Doe,89475,1/15/2023,(415) 555-1234,john.doe@test.com,Sales,Sales Strategy;Client Management +Jane,Smith,194669,2/10/2023,(408) 555-5678,jane.smith@test.com,Marketing,Market Research;Digital Marketing +Michael,Johnson,229772,3/5/2023,(650) 555-9876,michael.johnson@test.com,Finance,Financial Analysis;Budgeting +Emily,Brown,190682,4/20/2023,(213) 555-2468,emily.brown@test.com,Sales,Client Relationship;Sales Planning +William,Taylor,242917,5/3/2023,(714) 555-1357,william.taylor@test.com,Marketing,Content Creation;Social Media Marketing +Olivia,Anderson,83995,6/8/2023,(909) 555-7890,olivia.anderson@test.com,Finance,Financial Reporting;Risk Management +Benjamin,Lee,142247,7/11/2023,(925) 555-3698,benjamin.lee@test.com,Engineering,Software Development;Database Management +Sophia,Wilson,237992,8/17/2023,(562) 555-2580,sophia.wilson@test.com,Operations,Process Improvement;Supply Chain Management +Daniel,Clark,218336,9/25/2023,(650) 555-7410,daniel.clark@test.com,Marketing,Market Analysis;Campaign Management +Isabella,Walker,119856,10/30/2023,(916) 555-9632,isabella.walker@test.com,Finance,Financial Planning;Investment Analysis +Ethan,Hall,172377,11/12/2023,(818) 555-7531,ethan.hall@test.com,Engineering,Software Engineering;Systems Architecture +Mia,Lopez,203546,12/5/2023,(619) 555-4876,mia.lopez@test.com,Sales,Sales Operations;Lead Generation +James,Green,248710,1/1/2023,(408) 555-9517,james.green@test.com,Operations,Logistics Management;Inventory Control +Ava,Hernandez,190830,2/14/2023,(213) 555-3698,ava.hernandez@test.com,Marketing,Brand Management;Product Launch +Alexander,Young,97092,3/18/2023,(714) 555-8025,alexander.young@test.com,Finance,Financial Modeling;Cost Analysis +Charlotte,King,117417,4/22/2023,(925) 555-6479,charlotte.king@test.com,Engineering,Product Design;Quality Assurance +Logan,Hill,225291,5/27/2023,(562) 555-3216,logan.hill@test.com,Sales,Account Management;Negotiation +Amelia,Scott,183392,6/30/2023,(650) 555-4567,amelia.scott@test.com,Operations,Process Optimization;Facilities Management +Daniel,Lewis,103561,7/5/2023,(916) 555-7890,daniel.lewis@test.com,Engineering,Hardware Development;Embedded Systems +Abigail,Adams,225228,8/9/2023,(818) 555-8524,abigail.adams@test.com,Marketing,Marketing Strategy;Market Segmentation +Oliver,Allen,161033,9/13/2023,(619) 555-9632,oliver.allen@test.com,Finance,Financial Forecasting;Tax Planning +Sophia,Lee,248627,10/18/2023,(415) 555-1478,sophia.lee@test.com,Operations,Supply Chain Optimization;Process Automation +Emily,Harris,208887,11/21/2023,(408) 555-3698,emily.harris@test.com,Sales,Business Development;Key Account Management +Jacob,Turner,182322,12/25/2023,(213) 555-7531,jacob.turner@test.com,Marketing,SEO;Content Marketing +Madison,Wright,80164,1/29/2023,(714) 555-2580,madison.wright@test.com,Finance,Financial Compliance;Auditing +Lucas,Hill,223297,2/23/2023,(925) 555-9517,lucas.hill@test.com,Engineering,Network Engineering;Cloud Computing +Sofia,Thomas,127653,3/17/2023,(562) 555-3698,sofia.thomas@test.com,Operations,Project Management;Quality Management +Henry,Lopez,141701,4/10/2023,(650) 555-7410,henry.lopez@test.com,Sales,Inside Sales;Sales Forecasting +Avery,Adams,82895,5/4/2023,(916) 555-9632,avery.adams@test.com,Marketing,Influencer Marketing;Event Planning +Alexander,Green,215160,6/28/2023,(818) 555-7890,alexander.green@test.com,Finance,Financial Risk Management;Investment Banking +Ella,Baker,120032,7/22/2023,(619) 555-4567,ella.baker@test.com,Engineering,Data Science;Machine Learning +Jackson,Evans,139075,8/16/2023,(415) 555-8025,jackson.evans@test.com,Operations,Continuous Improvement;Lean Manufacturing +Scarlett,Gonzalez,175443,9/10/2023,(408) 555-3698,scarlett.gonzalez@test.com,Sales,Outside Sales;Sales Presentations +Aiden,Wright,154326,10/4/2023,(213) 555-9632,aiden.wright@test.com,Marketing,Market Trend Analysis;Brand Development +Penelope,Lee,230399,11/28/2023,(714) 555-1478,penelope.lee@test.com,Finance,Financial Valuation;Mergers and Acquisitions +Mason,Hernandez,178930,12/22/2023,(925) 555-3698,mason.hernandez@test.com,Engineering,Software Testing;Quality Assurance +Lily,Turner,160080,1/16/2023,(562) 555-8524,lily.turner@test.com,Operations,Inventory Management;Supplier Relationship Management +Liam,Scott,108406,2/9/2023,(650) 555-7410,liam.scott@test.com,Sales,Strategic Account Management;Sales Analytics +Chloe,Young,248731,3/5/2023,(916) 555-8524,chloe.young@test.com,Marketing,Content Strategy;Social Media Management +Jacob,Lopez,219674,4/29/2023,(818) 555-2580,jacob.lopez@test.com,Finance,Financial Planning;Financial Reporting +Grace,Allen,216574,5/23/2023,(619) 555-9517,grace.allen@test.com,Engineering,Software Development;Systems Engineering +Zoe,Hill,203918,6/17/2023,(415) 555-3698,zoe.hill@test.com,Operations,Logistics Planning;Process Standardization +Elijah,Harris,184617,7/11/2023,(408) 555-7531,elijah.harris@test.com,Sales,Lead Generation;Account Development +Aurora,King,230678,8/5/2023,(213) 555-8524,aurora.king@test.com,Marketing,Email Marketing;Digital Advertising +Carter,Adams,113398,9/29/2023,(714) 555-1478,carter.adams@test.com,Finance,Financial Analysis;Risk Assessment +Aria,Johnson,191577,10/24/2023,(925) 555-3698,aria.johnson@test.com,Engineering,Hardware Design;Embedded Systems +Luke,Walker,118605,11/18/2023,(562) 555-7410,luke.walker@test.com,Operations,Process Optimization;Supply Chain Analysis +Ruby,Thomas,143820,12/12/2023,(650) 555-9632,ruby.thomas@test.com,Sales,Sales Operations;Customer Relationship Management +Ethan,Brown,219040,1/5/2023,(916) 555-8524,ethan.brown@test.com,Marketing,Market Research;Product Management +Hannah,Evans,171432,2/28/2023,(818) 555-4567,hannah.evans@test.com,Finance,Financial Forecasting;Budgeting +Levi,Gonzalez,227072,3/24/2023,(619) 555-7410,levi.gonzalez@test.com,Engineering,Network Security;Database Management +Nora,Baker,195789,4/18/2023,(415) 555-9632,nora.baker@test.com,Operations,Project Planning;Facilities Planning +Jack,Wright,157791,5/12/2023,(408) 555-8524,jack.wright@test.com,Sales,Sales Strategy;Negotiation +Aubrey,Clark,176029,6/5/2023,(213) 555-3698,aubrey.clark@test.com,Marketing,Brand Strategy;Content Creation +Wyatt,Allen,125464,7/30/2023,(714) 555-7410,wyatt.allen@test.com,Finance,Financial Modeling;Investment Analysis +Addison,Hill,126447,8/24/2023,(925) 555-8524,addison.hill@test.com,Engineering,Product Design;Quality Assurance +Harper,Scott,147482,9/17/2023,(562) 555-9632,harper.scott@test.com,Operations,Process Improvement;Supply Chain Management +Thomas,Lopez,215337,10/11/2023,(650) 555-3698,thomas.lopez@test.com,Sales,Client Relationship;Sales Planning +Samantha,Green,85362,11/4/2023,(916) 555-7410,samantha.green@test.com,Marketing,Market Analysis;Campaign Management +Henry,Adams,238372,12/28/2023,(818) 555-8524,henry.adams@test.com,Finance,Financial Planning;Investment Analysis diff --git a/examples/EntityFrameworkCore.Ydb.Samples/README.md b/examples/EntityFrameworkCore.Ydb.Samples/README.md new file mode 100644 index 00000000..c582d104 --- /dev/null +++ b/examples/EntityFrameworkCore.Ydb.Samples/README.md @@ -0,0 +1,18 @@ +# Entity Framework Core Ydb Samples + +This project contains a number of samples for using Entity Framework Core in combination with YDB. + +Some samples are inspired by the [Entity Framework Core Tutorial](https://www.csharptutorial.net/entity-framework-core-tutorial). + +## Samples + +- **AddEntity.Sample** – Example for adding entities. +- **Database.Operations.Tutorial** – Basic database operations. +- **Schema.OneToOne** – One-to-one relationship. +- **Schema.OneToMany** – One-to-many relationship. +- **Schema.ManyToMany** – Many-to-many relationship. + +## Usage + +Each subfolder contains an independent sample. +Please refer to the README or documentation inside each sample folder for instructions on how to run it. diff --git a/examples/EntityFrameworkCore.Ydb.Samples/Schema.ManyToMany/HRContext.cs b/examples/EntityFrameworkCore.Ydb.Samples/Schema.ManyToMany/HRContext.cs new file mode 100644 index 00000000..810242ac --- /dev/null +++ b/examples/EntityFrameworkCore.Ydb.Samples/Schema.ManyToMany/HRContext.cs @@ -0,0 +1,35 @@ +using EntityFrameworkCore.Ydb.Extensions; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Design; +using Microsoft.EntityFrameworkCore.Diagnostics; + +namespace Schema.ManyToMany; + +public class HRContext(DbContextOptions options) : DbContext(options) +{ + public DbSet Employees { get; set; } + public DbSet Departments { get; set; } +} + +internal class HRContextContextFactory : IDesignTimeDbContextFactory +{ + public HRContext CreateDbContext(string[] args) + { + var optionsBuilder = new DbContextOptionsBuilder(); + + // IMPORTANT! + // Disables retries for the migrations context. + // Required because migration operations may use suppressed or explicit transactions, + // and enabling retries in this case leads to runtime errors with this provider. + // + // "System.NotSupportedException: User transaction is not supported with a TransactionSuppressed migrations or a retrying execution strategy." + // + // Bottom line: ALWAYS disable retries for design-time/migration contexts to avoid migration failures and errors. + return new HRContext( + optionsBuilder + .UseYdb("Host=localhost;Port=2136;Database=/local", builder => builder.DisableRetryOnFailure()) + .ConfigureWarnings(warnings => warnings.Ignore(RelationalEventId.PendingModelChangesWarning)) + .Options + ); + } +} \ No newline at end of file diff --git a/examples/EntityFrameworkCore.Ydb.Samples/Schema.ManyToMany/Models.cs b/examples/EntityFrameworkCore.Ydb.Samples/Schema.ManyToMany/Models.cs new file mode 100644 index 00000000..7acb76a5 --- /dev/null +++ b/examples/EntityFrameworkCore.Ydb.Samples/Schema.ManyToMany/Models.cs @@ -0,0 +1,51 @@ +namespace Schema.ManyToMany; + +public class Department +{ + public int Id { get; set; } + public required string Name { get; set; } + + // Collection navigation containing children + public required ICollection Employees { get; set; } +} + +public class Employee +{ + public int Id { get; set; } + public required string FirstName { get; set; } + public required string LastName { get; set; } + public required decimal Salary { get; set; } + public required DateTime JoinedDate { get; set; } + public int DepartmentId { get; set; } + + // Reference navigation to Department + public Department Department { get; set; } = null!; + + // Reference navigation to EmployeeProfile + public EmployeeProfile? Profile { get; set; } + + // collection navigation to Employee + public List Skills { get; set; } = new(); +} + +public class EmployeeProfile +{ + public int Id { get; set; } + public required string Phone { get; set; } + public required string Email { get; set; } + + // Required foreign key property + public int EmployeeId { get; set; } + + // Required reference navigation to Employee + public Employee Employee { get; set; } = null!; +} + +public class Skill +{ + public int Id { get; set; } + public required string Title { get; set; } + + // collection navigation to Employee + public List Employees { get; set; } = new(); +} \ No newline at end of file diff --git a/examples/EntityFrameworkCore.Ydb.Samples/Schema.ManyToMany/Program.cs b/examples/EntityFrameworkCore.Ydb.Samples/Schema.ManyToMany/Program.cs new file mode 100644 index 00000000..9cbe6ada --- /dev/null +++ b/examples/EntityFrameworkCore.Ydb.Samples/Schema.ManyToMany/Program.cs @@ -0,0 +1 @@ +Console.WriteLine(); \ No newline at end of file diff --git a/examples/EntityFrameworkCore.Ydb.Samples/Schema.ManyToMany/README.md b/examples/EntityFrameworkCore.Ydb.Samples/Schema.ManyToMany/README.md new file mode 100644 index 00000000..b12291c7 --- /dev/null +++ b/examples/EntityFrameworkCore.Ydb.Samples/Schema.ManyToMany/README.md @@ -0,0 +1,28 @@ +# Entity Framework Core YDB Schema Many-to-Many Example + +This sample demonstrates how to model a [many-to-many relationship](https://www.csharptutorial.net/entity-framework-core-tutorial/ef-core-many-to-many-relationships/) in Entity Framework Core targeting YDB. + +The focus here is to show what database schema (DDL) will be generated by EF Core migrations for such a model. + +## How to View the Generated Schema + +1. Set up a local YDB instance: + [YDB local setup guide](https://ydb.tech/docs/en/reference/docker/start) + +2. Install the EF Core CLI tool (if needed): + ```bash + dotnet tool install --global dotnet-ef + dotnet add package Microsoft.EntityFrameworkCore.Design + ``` + +3. Add a migration to generate the DDL (if not already present): + ```bash + dotnet ef migrations add InitialCreate + ``` + +4. Generate the SQL script for the schema: + ```bash + dotnet ef migrations script + ``` + +5. Inspect the generated SQL script to see how EF Core maps the many-to-many relationship in YDB. diff --git a/examples/EntityFrameworkCore.Ydb.Samples/Schema.ManyToMany/Schema.ManyToMany.csproj b/examples/EntityFrameworkCore.Ydb.Samples/Schema.ManyToMany/Schema.ManyToMany.csproj new file mode 100644 index 00000000..4449f9bb --- /dev/null +++ b/examples/EntityFrameworkCore.Ydb.Samples/Schema.ManyToMany/Schema.ManyToMany.csproj @@ -0,0 +1,21 @@ + + + + Exe + net8.0 + enable + enable + Schema.ManyToMany + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + diff --git a/examples/EntityFrameworkCore.Ydb.Samples/Schema.OneToMany/HRContext.cs b/examples/EntityFrameworkCore.Ydb.Samples/Schema.OneToMany/HRContext.cs new file mode 100644 index 00000000..85d99eaa --- /dev/null +++ b/examples/EntityFrameworkCore.Ydb.Samples/Schema.OneToMany/HRContext.cs @@ -0,0 +1,37 @@ +// using Microsoft.EntityFrameworkCore; + +using EntityFrameworkCore.Ydb.Extensions; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Design; +using Microsoft.EntityFrameworkCore.Diagnostics; + +namespace Schema.OneToMany; + +public class HRContext(DbContextOptions options) : DbContext(options) +{ + public DbSet Employees { get; set; } + public DbSet Departments { get; set; } +} + +internal class HRContextContextFactory : IDesignTimeDbContextFactory +{ + public HRContext CreateDbContext(string[] args) + { + var optionsBuilder = new DbContextOptionsBuilder(); + + // IMPORTANT! + // Disables retries for the migrations context. + // Required because migration operations may use suppressed or explicit transactions, + // and enabling retries in this case leads to runtime errors with this provider. + // + // "System.NotSupportedException: User transaction is not supported with a TransactionSuppressed migrations or a retrying execution strategy." + // + // Bottom line: ALWAYS disable retries for design-time/migration contexts to avoid migration failures and errors. + return new HRContext( + optionsBuilder + .UseYdb("Host=localhost;Port=2136;Database=/local", builder => builder.DisableRetryOnFailure()) + .ConfigureWarnings(warnings => warnings.Ignore(RelationalEventId.PendingModelChangesWarning)) + .Options + ); + } +} \ No newline at end of file diff --git a/examples/EntityFrameworkCore.Ydb.Samples/Schema.OneToMany/Models.cs b/examples/EntityFrameworkCore.Ydb.Samples/Schema.OneToMany/Models.cs new file mode 100644 index 00000000..a05bcec5 --- /dev/null +++ b/examples/EntityFrameworkCore.Ydb.Samples/Schema.OneToMany/Models.cs @@ -0,0 +1,25 @@ +namespace Schema.OneToMany; + +public class Department +{ + public int Id { get; set; } + public required string Name { get; set; } + + // Collection navigation containing children + public required ICollection Employees { get; set; } +} + +public class Employee +{ + public int Id { get; set; } + public required string FirstName { get; set; } + public required string LastName { get; set; } + public required decimal Salary { get; set; } + public required DateTime JoinedDate { get; set; } + + // Required foreign key property + public int DepartmentId { get; set; } + + // Required reference navigation to parent + public Department Department { get; set; } = null!; +} \ No newline at end of file diff --git a/examples/EntityFrameworkCore.Ydb.Samples/Schema.OneToMany/Program.cs b/examples/EntityFrameworkCore.Ydb.Samples/Schema.OneToMany/Program.cs new file mode 100644 index 00000000..9cbe6ada --- /dev/null +++ b/examples/EntityFrameworkCore.Ydb.Samples/Schema.OneToMany/Program.cs @@ -0,0 +1 @@ +Console.WriteLine(); \ No newline at end of file diff --git a/examples/EntityFrameworkCore.Ydb.Samples/Schema.OneToMany/README.md b/examples/EntityFrameworkCore.Ydb.Samples/Schema.OneToMany/README.md new file mode 100644 index 00000000..f1a9d02f --- /dev/null +++ b/examples/EntityFrameworkCore.Ydb.Samples/Schema.OneToMany/README.md @@ -0,0 +1,27 @@ +# Entity Framework Core YDB Schema One-to-Many Example + +This sample demonstrates how to model a [one-to-many relationship](https://www.csharptutorial.net/entity-framework-core-tutorial/ef-core-one-to-many-relationship/) in Entity Framework Core targeting YDB. + +The focus here is to show what database schema (DDL) will be generated by EF Core migrations for such a model. + +## How to View the Generated Schema + +1. Set up [YDB local](https://ydb.tech/docs/en/reference/docker/start). + +2. Install the EF Core CLI tool (if needed): + ```bash + dotnet tool install --global dotnet-ef + dotnet add package Microsoft.EntityFrameworkCore.Design + ``` + +3. Add a migration to generate the DDL (if not already present): + ```bash + dotnet ef migrations add InitialCreate + ``` + +4. Generate the SQL script for the schema: + ```bash + dotnet ef migrations script + ``` + +5. Inspect the generated SQL script to see how EF Core maps the one-to-many relationship in YDB. diff --git a/examples/EntityFrameworkCore.Ydb.Samples/Schema.OneToMany/Schema.OneToMany.csproj b/examples/EntityFrameworkCore.Ydb.Samples/Schema.OneToMany/Schema.OneToMany.csproj new file mode 100644 index 00000000..29520a74 --- /dev/null +++ b/examples/EntityFrameworkCore.Ydb.Samples/Schema.OneToMany/Schema.OneToMany.csproj @@ -0,0 +1,21 @@ + + + + Exe + net8.0 + enable + enable + Schema.OneToMany + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + diff --git a/examples/EntityFrameworkCore.Ydb.Samples/Schema.OneToOne/HRContext.cs b/examples/EntityFrameworkCore.Ydb.Samples/Schema.OneToOne/HRContext.cs new file mode 100644 index 00000000..7f78fa3e --- /dev/null +++ b/examples/EntityFrameworkCore.Ydb.Samples/Schema.OneToOne/HRContext.cs @@ -0,0 +1,37 @@ +// using Microsoft.EntityFrameworkCore; + +using EntityFrameworkCore.Ydb.Extensions; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Design; +using Microsoft.EntityFrameworkCore.Diagnostics; + +namespace Schema.OneToOne; + +public class HRContext(DbContextOptions options) : DbContext(options) +{ + public DbSet Employees { get; set; } + public DbSet Departments { get; set; } +} + +internal class HRContextContextFactory : IDesignTimeDbContextFactory +{ + public HRContext CreateDbContext(string[] args) + { + var optionsBuilder = new DbContextOptionsBuilder(); + + // IMPORTANT! + // Disables retries for the migrations context. + // Required because migration operations may use suppressed or explicit transactions, + // and enabling retries in this case leads to runtime errors with this provider. + // + // "System.NotSupportedException: User transaction is not supported with a TransactionSuppressed migrations or a retrying execution strategy." + // + // Bottom line: ALWAYS disable retries for design-time/migration contexts to avoid migration failures and errors. + return new HRContext( + optionsBuilder + .UseYdb("Host=localhost;Port=2136;Database=/local", builder => builder.DisableRetryOnFailure()) + .ConfigureWarnings(warnings => warnings.Ignore(RelationalEventId.PendingModelChangesWarning)) + .Options + ); + } +} \ No newline at end of file diff --git a/examples/EntityFrameworkCore.Ydb.Samples/Schema.OneToOne/Models.cs b/examples/EntityFrameworkCore.Ydb.Samples/Schema.OneToOne/Models.cs new file mode 100644 index 00000000..60037e96 --- /dev/null +++ b/examples/EntityFrameworkCore.Ydb.Samples/Schema.OneToOne/Models.cs @@ -0,0 +1,39 @@ +namespace Schema.OneToOne; + +public class Department +{ + public int Id { get; set; } + public required string Name { get; set; } + + // Collection navigation containing children + public required ICollection Employees { get; set; } +} + +public class Employee +{ + public int Id { get; set; } + public required string FirstName { get; set; } + public required string LastName { get; set; } + public required decimal Salary { get; set; } + public required DateTime JoinedDate { get; set; } + public int DepartmentId { get; set; } + + // Reference navigation to Department + public Department Department { get; set; } = null!; + + // Reference navigation to EmployeeProfile + public EmployeeProfile? Profile { get; set; } +} + +public class EmployeeProfile +{ + public int Id { get; set; } + public required string Phone { get; set; } + public required string Email { get; set; } + + // Required foreign key property + public int EmployeeId { get; set; } + + // Required reference navigation to Employee + public Employee Employee { get; set; } = null!; +} \ No newline at end of file diff --git a/examples/EntityFrameworkCore.Ydb.Samples/Schema.OneToOne/Program.cs b/examples/EntityFrameworkCore.Ydb.Samples/Schema.OneToOne/Program.cs new file mode 100644 index 00000000..864ddc49 --- /dev/null +++ b/examples/EntityFrameworkCore.Ydb.Samples/Schema.OneToOne/Program.cs @@ -0,0 +1 @@ +Console.WriteLine(); \ No newline at end of file diff --git a/examples/EntityFrameworkCore.Ydb.Samples/Schema.OneToOne/README.md b/examples/EntityFrameworkCore.Ydb.Samples/Schema.OneToOne/README.md new file mode 100644 index 00000000..5609e498 --- /dev/null +++ b/examples/EntityFrameworkCore.Ydb.Samples/Schema.OneToOne/README.md @@ -0,0 +1,27 @@ +# Entity Framework Core YDB Schema One-to-One Example + +This sample demonstrates how to model a [one-to-one relationship](https://www.csharptutorial.net/entity-framework-core-tutorial/ef-core-one-to-one/) in Entity Framework Core targeting YDB. + +The focus here is to show what database schema (DDL) will be generated by EF Core migrations for such a model. + +## How to View the Generated Schema + +1. Set up [YDB local](https://ydb.tech/docs/en/reference/docker/start). + +2. Install the EF Core CLI tool (if needed): + ```bash + dotnet tool install --global dotnet-ef + dotnet add package Microsoft.EntityFrameworkCore.Design + ``` + +3. Add a migration to generate the DDL (if not already present): + ```bash + dotnet ef migrations add InitialCreate + ``` + +4. Generate the SQL script for the schema: + ```bash + dotnet ef migrations script + ``` + +5. Inspect the generated SQL script to see how EF Core maps the one-to-one relationship in YDB. diff --git a/examples/EntityFrameworkCore.Ydb.Samples/Schema.OneToOne/Schema.OneToOne.csproj b/examples/EntityFrameworkCore.Ydb.Samples/Schema.OneToOne/Schema.OneToOne.csproj new file mode 100644 index 00000000..484e670b --- /dev/null +++ b/examples/EntityFrameworkCore.Ydb.Samples/Schema.OneToOne/Schema.OneToOne.csproj @@ -0,0 +1,21 @@ + + + + Exe + net8.0 + enable + enable + Schema.OneToOne + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + diff --git a/examples/src/EntityFrameworkCore.Ydb.Yandex.Cloud/CmdOptions.cs b/examples/EntityFrameworkCore.Ydb.Yandex.Cloud/CmdOptions.cs similarity index 100% rename from examples/src/EntityFrameworkCore.Ydb.Yandex.Cloud/CmdOptions.cs rename to examples/EntityFrameworkCore.Ydb.Yandex.Cloud/CmdOptions.cs diff --git a/examples/src/EntityFrameworkCore.Ydb.Yandex.Cloud/EntityFrameworkCore.Ydb.Yandex.Cloud.csproj b/examples/EntityFrameworkCore.Ydb.Yandex.Cloud/EntityFrameworkCore.Ydb.Yandex.Cloud.csproj similarity index 80% rename from examples/src/EntityFrameworkCore.Ydb.Yandex.Cloud/EntityFrameworkCore.Ydb.Yandex.Cloud.csproj rename to examples/EntityFrameworkCore.Ydb.Yandex.Cloud/EntityFrameworkCore.Ydb.Yandex.Cloud.csproj index 7c01b52e..a6522c29 100644 --- a/examples/src/EntityFrameworkCore.Ydb.Yandex.Cloud/EntityFrameworkCore.Ydb.Yandex.Cloud.csproj +++ b/examples/EntityFrameworkCore.Ydb.Yandex.Cloud/EntityFrameworkCore.Ydb.Yandex.Cloud.csproj @@ -16,7 +16,6 @@ - - + diff --git a/examples/src/EntityFrameworkCore.Ydb.Yandex.Cloud/Program.cs b/examples/EntityFrameworkCore.Ydb.Yandex.Cloud/Program.cs similarity index 100% rename from examples/src/EntityFrameworkCore.Ydb.Yandex.Cloud/Program.cs rename to examples/EntityFrameworkCore.Ydb.Yandex.Cloud/Program.cs diff --git a/examples/src/EntityFrameworkCore.Ydb.Yandex.Cloud/README.md b/examples/EntityFrameworkCore.Ydb.Yandex.Cloud/README.md similarity index 100% rename from examples/src/EntityFrameworkCore.Ydb.Yandex.Cloud/README.md rename to examples/EntityFrameworkCore.Ydb.Yandex.Cloud/README.md diff --git a/examples/README.md b/examples/README.md index b1ff27e6..aa581d29 100644 --- a/examples/README.md +++ b/examples/README.md @@ -1,35 +1,13 @@ -# YDB .NET SDK Examples +# YDB .NET Ecosystem Examples -## Prerequisites -.NET 6 +Sample projects for working with YDB in .NET. -## Running examples +## Projects -1. Clone repository - ```bash - git clone https://github.com/ydb-platform/ydb-dotnet-sdk.git - ``` - -2. Build solution - ```bash - cd ydb-dotnet-sdk/examples/src - dotnet build - ``` - -3. Run example - ```bash - cd - dotnet run -e -d - ``` - -## Provided examples - -### BasicExample -Demonstrates basic operations with YDB, including: -* Driver initialization -* Table client initialization -* Table creation via SchemeQuery (DDL) -* Data queries (OLTP) & transactions (read, modify) -* Interactive transactions -* ReadTable for streaming read of table contents -* ScanQuery for streaming wide queries (OLAP) +- **Ydb.Sdk.AdoNet.QuickStart** – Quickstart for using YDB with ADO.NET. +- **Ydb.Sdk.AdoNet.Dapper.QuickStart** – Example of Dapper usage with YDB. +- **Ydb.Sdk.AdoNet.Yandex.Cloud** – Connecting to YDB in Yandex Cloud via ADO.NET. +- **EntityFrameworkCore.Ydb.QuickStart** – Quickstart for YDB + Entity Framework Core. +- **EntityFrameworkCore.Ydb.Samples** – Extended Entity Framework Core samples. +- **EntityFrameworkCore.Ydb.Yandex.Cloud** – Example of EF Core with YDB in Yandex Cloud. +- **Ydb.Sdk.Topic.QuickStart** – Example for Topic API. diff --git a/examples/src/DapperExample/Program.cs b/examples/Ydb.Sdk.AdoNet.Dapper.QuickStart/Program.cs similarity index 99% rename from examples/src/DapperExample/Program.cs rename to examples/Ydb.Sdk.AdoNet.Dapper.QuickStart/Program.cs index bbcde0da..4e050913 100644 --- a/examples/src/DapperExample/Program.cs +++ b/examples/Ydb.Sdk.AdoNet.Dapper.QuickStart/Program.cs @@ -6,6 +6,7 @@ // Init Users table await using var connection = await new YdbDataSource().OpenConnectionAsync(); +await connection.ExecuteAsync("DROP TABLE IF EXISTS Users"); await connection.ExecuteAsync(""" CREATE TABLE IF NOT EXISTS Users( Id Int32, @@ -21,8 +22,6 @@ await connection.ExecuteAsync("INSERT INTO Users(Id, Name, Email) VALUES (@Id, @ Console.WriteLine(await connection.QuerySingleAsync("SELECT * FROM Users WHERE Id = @Id", new { Id = 1 })); -await connection.ExecuteAsync("DROP TABLE IF EXISTS Users"); - internal class User { public int Id { get; init; } diff --git a/examples/Ydb.Sdk.AdoNet.Dapper.QuickStart/README.md b/examples/Ydb.Sdk.AdoNet.Dapper.QuickStart/README.md new file mode 100644 index 00000000..72600d4a --- /dev/null +++ b/examples/Ydb.Sdk.AdoNet.Dapper.QuickStart/README.md @@ -0,0 +1,13 @@ +# YDB Sdk ADO.NET Dapper Quick Start + +A sample application demonstrating simple usage of Dapper with the YDB ADO.NET provider. +Shows how to perform basic database queries and map results to C# objects using Dapper. + +## Running QuickStart + +1. Set up [YDB local](https://ydb.tech/docs/en/reference/docker/start). + +2. Run the app: + ```bash + dotnet run + ``` diff --git a/examples/src/DapperExample/DapperExample.csproj b/examples/Ydb.Sdk.AdoNet.Dapper.QuickStart/Ydb.Sdk.AdoNet.Dapper.QuickStart.csproj similarity index 72% rename from examples/src/DapperExample/DapperExample.csproj rename to examples/Ydb.Sdk.AdoNet.Dapper.QuickStart/Ydb.Sdk.AdoNet.Dapper.QuickStart.csproj index e1d9c706..868b1300 100644 --- a/examples/src/DapperExample/DapperExample.csproj +++ b/examples/Ydb.Sdk.AdoNet.Dapper.QuickStart/Ydb.Sdk.AdoNet.Dapper.QuickStart.csproj @@ -5,11 +5,11 @@ net8.0 enable enable - Dapper + Ydb.Sdk.AdoNet.Dapper.QuickStart - + diff --git a/examples/src/AdoNet/CmdOptions.cs b/examples/Ydb.Sdk.AdoNet.QuickStart/CmdOptions.cs similarity index 95% rename from examples/src/AdoNet/CmdOptions.cs rename to examples/Ydb.Sdk.AdoNet.QuickStart/CmdOptions.cs index 00b2100f..f2b0b4b9 100644 --- a/examples/src/AdoNet/CmdOptions.cs +++ b/examples/Ydb.Sdk.AdoNet.QuickStart/CmdOptions.cs @@ -1,6 +1,6 @@ using CommandLine; -namespace AdoNet; +namespace Ydb.Sdk.AdoNet.QuickStart; internal class CmdOptions { diff --git a/examples/src/AdoNet/Program.cs b/examples/Ydb.Sdk.AdoNet.QuickStart/Program.cs similarity index 97% rename from examples/src/AdoNet/Program.cs rename to examples/Ydb.Sdk.AdoNet.QuickStart/Program.cs index c0e3b533..bba59fd5 100644 --- a/examples/src/AdoNet/Program.cs +++ b/examples/Ydb.Sdk.AdoNet.QuickStart/Program.cs @@ -1,9 +1,9 @@ using System.Data; -using AdoNet; using CommandLine; using Microsoft.Extensions.Logging; using Polly; using Ydb.Sdk.Ado; +using Ydb.Sdk.AdoNet.QuickStart; using var factory = LoggerFactory.Create(builder => builder.AddConsole()); @@ -82,8 +82,6 @@ CREATE TABLE episodes PRIMARY KEY (series_id, season_id, episode_id) ); """; - _logger.LogInformation("Creating tables for examples, SQL script: {CommandText}", ydbCommand.CommandText); - await ydbCommand.ExecuteNonQueryAsync(); _logger.LogInformation("Created tables"); @@ -185,8 +183,6 @@ UPSERT INTO episodes (series_id, season_id, episode_id, title, air_date) (2, 5, 7, "Initial Coin Offering", Date("2018-05-06")), (2, 5, 8, "Fifty-One Percent", Date("2018-05-13")); """; - _logger.LogInformation("Loading data for examples, SQL script: {CommandText}", ydbCommand.CommandText); - await ydbCommand.ExecuteNonQueryAsync(); _logger.LogInformation("Loaded data"); @@ -225,8 +221,6 @@ ORDER BY -- Sorting the results. ydbCommand.Parameters.Add(new YdbParameter("$season_id", DbType.UInt64, 1U)); ydbCommand.Parameters.Add(new YdbParameter("$limit_size", DbType.UInt64, 3U)); - _logger.LogInformation("Selecting data, SQL script: {CommandText}", ydbCommand.CommandText); - var ydbDataReader = await ydbCommand.ExecuteReaderAsync(); _logger.LogInformation("Selected rows:"); @@ -312,16 +306,13 @@ private async Task InteractiveTransaction() UPSERT INTO episodes (series_id, season_id, episode_id, title, air_date) VALUES (2, 5, 13, "Test Episode", Date("2018-08-27")) """; - _logger.LogInformation("Updating data, SQL script: {CommandText}", ydbCommand.CommandText); await ydbCommand.ExecuteNonQueryAsync(); - ydbCommand.CommandText = """ INSERT INTO episodes(series_id, season_id, episode_id, title, air_date) VALUES (2, 5, 21, "Test 21", Date("2018-08-27")), (2, 5, 22, "Test 22", Date("2018-08-27")) """; - _logger.LogInformation("Inserting data, SQL script: {CommandText}", ydbCommand.CommandText); await ydbCommand.ExecuteNonQueryAsync(); await ydbCommand.Transaction.CommitAsync(); _logger.LogInformation("Commit transaction"); @@ -405,7 +396,7 @@ private async Task ConnectionWithLoggerFactory() { Host = _cmdOptions.Host, Port = _cmdOptions.Port, - LoggerFactory = LoggerFactory.Create(builder => builder.AddConsole().SetMinimumLevel(LogLevel.Debug)) + LoggerFactory = LoggerFactory.Create(builder => builder.AddConsole().SetMinimumLevel(LogLevel.Information)) }; await using var ydbConnection = new YdbConnection(connectionStringBuilder); await ydbConnection.OpenAsync(); diff --git a/examples/Ydb.Sdk.AdoNet.QuickStart/README.md b/examples/Ydb.Sdk.AdoNet.QuickStart/README.md new file mode 100644 index 00000000..377cb9ba --- /dev/null +++ b/examples/Ydb.Sdk.AdoNet.QuickStart/README.md @@ -0,0 +1,14 @@ +# YDB Sdk ADO.NET Quick Start + +A sample application demonstrating how to use YDB with ADO.NET. +Shows how to create tables, execute interactive transactions, and perform basic database operations using the standard +ADO.NET API. + +## Running QuickStart + +1. Set up [YDB local](https://ydb.tech/docs/en/reference/docker/start). + +2. Run the app: + ```bash + dotnet run + ``` diff --git a/examples/src/AdoNet/AdoNet.csproj b/examples/Ydb.Sdk.AdoNet.QuickStart/Ydb.Sdk.AdoNet.QuickStart.csproj similarity index 81% rename from examples/src/AdoNet/AdoNet.csproj rename to examples/Ydb.Sdk.AdoNet.QuickStart/Ydb.Sdk.AdoNet.QuickStart.csproj index 2f0be3a9..54d438b6 100644 --- a/examples/src/AdoNet/AdoNet.csproj +++ b/examples/Ydb.Sdk.AdoNet.QuickStart/Ydb.Sdk.AdoNet.QuickStart.csproj @@ -5,10 +5,11 @@ net8.0 enable enable + Ydb.Sdk.AdoNet.QuickStart - + diff --git a/examples/Ydb.Sdk.AdoNet.Yandex.Cloud/CmdOptions.cs b/examples/Ydb.Sdk.AdoNet.Yandex.Cloud/CmdOptions.cs new file mode 100644 index 00000000..00bb0df5 --- /dev/null +++ b/examples/Ydb.Sdk.AdoNet.Yandex.Cloud/CmdOptions.cs @@ -0,0 +1,12 @@ +using CommandLine; + +namespace Ydb.Sdk.AdoNet.Yandex.Cloud; + +internal class CmdOptions +{ + [Option("connectionString", Required = true, HelpText = "ConnectionString ADO.NET format")] + public string ConnectionString { get; set; } = null!; + + [Option("saFilePath", Required = true, HelpText = "Sa Key")] + public string SaFilePath { get; set; } = null!; +} \ No newline at end of file diff --git a/examples/src/YC/Program.cs b/examples/Ydb.Sdk.AdoNet.Yandex.Cloud/Program.cs similarity index 78% rename from examples/src/YC/Program.cs rename to examples/Ydb.Sdk.AdoNet.Yandex.Cloud/Program.cs index 5686b9cf..a504a4d2 100644 --- a/examples/src/YC/Program.cs +++ b/examples/Ydb.Sdk.AdoNet.Yandex.Cloud/Program.cs @@ -1,7 +1,7 @@ using CommandLine; using Microsoft.Extensions.Logging; -using YcCloud; using Ydb.Sdk.Ado; +using Ydb.Sdk.AdoNet.Yandex.Cloud; using Ydb.Sdk.Yc; await Parser.Default.ParseArguments(args).WithParsedAsync(async cmd => @@ -10,12 +10,8 @@ await Parser.Default.ParseArguments(args).WithParsedAsync(async cmd var saProvider = new ServiceAccountProvider(saFilePath: cmd.SaFilePath, loggerFactory: loggerFactory); - var builder = new YdbConnectionStringBuilder + var builder = new YdbConnectionStringBuilder(cmd.ConnectionString) { - UseTls = true, - Host = cmd.Host, - Port = 2135, - Database = cmd.Database, CredentialsProvider = saProvider, LoggerFactory = loggerFactory, ServerCertificates = YcCerts.GetYcServerCertificates() @@ -24,6 +20,6 @@ await Parser.Default.ParseArguments(args).WithParsedAsync(async cmd await using var ydbConnection = new YdbConnection(builder); await ydbConnection.OpenAsync(); - Console.WriteLine(await new YdbCommand(ydbConnection) { CommandText = "SELECT 'Hello Dedicated YDB!'u" } + Console.WriteLine(await new YdbCommand(ydbConnection) { CommandText = "SELECT 'Hello YDB from Yandex Cloud!'u" } .ExecuteScalarAsync()); }); \ No newline at end of file diff --git a/examples/Ydb.Sdk.AdoNet.Yandex.Cloud/README.md b/examples/Ydb.Sdk.AdoNet.Yandex.Cloud/README.md new file mode 100644 index 00000000..78a69b38 --- /dev/null +++ b/examples/Ydb.Sdk.AdoNet.Yandex.Cloud/README.md @@ -0,0 +1,9 @@ +# YDB Sdk ADO.NET to Yandex Cloud Example + +A sample application that checks connection settings and performs a simple select query on a YDB database hosted in Yandex Cloud. + +## Running + +```bash +dotnet run --connectionString "UseTls=true;Host=;Port=2135;Database=" --saFilePath "" +``` diff --git a/examples/src/YC/YC.csproj b/examples/Ydb.Sdk.AdoNet.Yandex.Cloud/Ydb.Sdk.AdoNet.Yandex.Cloud.csproj similarity index 83% rename from examples/src/YC/YC.csproj rename to examples/Ydb.Sdk.AdoNet.Yandex.Cloud/Ydb.Sdk.AdoNet.Yandex.Cloud.csproj index 5e882a24..5702f8e1 100644 --- a/examples/src/YC/YC.csproj +++ b/examples/Ydb.Sdk.AdoNet.Yandex.Cloud/Ydb.Sdk.AdoNet.Yandex.Cloud.csproj @@ -5,7 +5,7 @@ net8.0 enable enable - YcCloud + Ydb.Sdk.AdoNet.Yandex.Cloud @@ -16,6 +16,6 @@ - + diff --git a/examples/src/Common/DataUtils.cs b/examples/Ydb.Sdk.QueryService.QuickStart/DataUtils.cs similarity index 99% rename from examples/src/Common/DataUtils.cs rename to examples/Ydb.Sdk.QueryService.QuickStart/DataUtils.cs index 6cf9b191..b65a7967 100644 --- a/examples/src/Common/DataUtils.cs +++ b/examples/Ydb.Sdk.QueryService.QuickStart/DataUtils.cs @@ -1,6 +1,3 @@ -using System; -using System.Collections.Generic; -using System.Linq; using Ydb.Sdk.Value; namespace Ydb.Sdk.Examples; diff --git a/examples/src/QueryExample/Program.cs b/examples/Ydb.Sdk.QueryService.QuickStart/Program.cs similarity index 87% rename from examples/src/QueryExample/Program.cs rename to examples/Ydb.Sdk.QueryService.QuickStart/Program.cs index 8e8d8b0e..7e036265 100644 --- a/examples/src/QueryExample/Program.cs +++ b/examples/Ydb.Sdk.QueryService.QuickStart/Program.cs @@ -39,10 +39,6 @@ private static async Task Run(CmdOptions cmdOptions) await QueryExample.Run( endpoint: cmdOptions.Endpoint, database: cmdOptions.Database, - credentialsProvider: await AuthUtils.MakeCredentialsFromEnv( - fallbackAnonymous: cmdOptions.FallbackAnonymous, - loggerFactory: loggerFactory), - customServerCertificate: AuthUtils.GetCustomServerCertificate(), path: cmdOptions.Path, loggerFactory: loggerFactory ); diff --git a/examples/src/QueryExample/QueryExample.cs b/examples/Ydb.Sdk.QueryService.QuickStart/QueryExample.cs similarity index 96% rename from examples/src/QueryExample/QueryExample.cs rename to examples/Ydb.Sdk.QueryService.QuickStart/QueryExample.cs index 1f66afa1..46e12400 100644 --- a/examples/src/QueryExample/QueryExample.cs +++ b/examples/Ydb.Sdk.QueryService.QuickStart/QueryExample.cs @@ -1,6 +1,4 @@ -using System.Security.Cryptography.X509Certificates; using Microsoft.Extensions.Logging; -using Ydb.Sdk.Auth; using Ydb.Sdk.Services.Query; using Ydb.Sdk.Value; @@ -20,17 +18,10 @@ private QueryExample(QueryClient client, string database, string path) public static async Task Run( string endpoint, string database, - ICredentialsProvider? credentialsProvider, - X509Certificate? customServerCertificate, string path, ILoggerFactory loggerFactory) { - var config = new DriverConfig( - endpoint: endpoint, - database: database, - credentials: credentialsProvider, - customServerCertificate: customServerCertificate - ); + var config = new DriverConfig(endpoint: endpoint, database: database); await using var driver = await Driver.CreateInitialized( config: config, diff --git a/examples/src/QueryExample/QueryExample.csproj b/examples/Ydb.Sdk.QueryService.QuickStart/Ydb.Sdk.QueryService.QuickStart.csproj similarity index 88% rename from examples/src/QueryExample/QueryExample.csproj rename to examples/Ydb.Sdk.QueryService.QuickStart/Ydb.Sdk.QueryService.QuickStart.csproj index 6e487ba2..4e349d82 100644 --- a/examples/src/QueryExample/QueryExample.csproj +++ b/examples/Ydb.Sdk.QueryService.QuickStart/Ydb.Sdk.QueryService.QuickStart.csproj @@ -8,7 +8,7 @@ Ydb.Sdk.Examples.QueryExample Ydb.Sdk.Examples - + git https://github.com/ydb-platform/ydb-dotnet-sdk @@ -17,12 +17,11 @@ - - + + - + - diff --git a/examples/src/Topic/Program.cs b/examples/Ydb.Sdk.Topic.QuickStart/Program.cs similarity index 100% rename from examples/src/Topic/Program.cs rename to examples/Ydb.Sdk.Topic.QuickStart/Program.cs diff --git a/examples/Ydb.Sdk.Topic.QuickStart/README.md b/examples/Ydb.Sdk.Topic.QuickStart/README.md new file mode 100644 index 00000000..ae999750 --- /dev/null +++ b/examples/Ydb.Sdk.Topic.QuickStart/README.md @@ -0,0 +1,12 @@ +# YDB SDK Topic Quick Start + +This sample demonstrates how to produce and consume messages using the YDB Topic API in .NET. + +## Running QuickStart + +1. Set up a local YDB instance: [YDB local setup](https://ydb.tech/docs/en/reference/docker/start) + +2. Run the sample: + ```bash + dotnet run + ``` diff --git a/examples/src/Topic/Topic.csproj b/examples/Ydb.Sdk.Topic.QuickStart/Ydb.Sdk.Topic.QuickStart.csproj similarity index 71% rename from examples/src/Topic/Topic.csproj rename to examples/Ydb.Sdk.Topic.QuickStart/Ydb.Sdk.Topic.QuickStart.csproj index b6cf30e9..59de5261 100644 --- a/examples/src/Topic/Topic.csproj +++ b/examples/Ydb.Sdk.Topic.QuickStart/Ydb.Sdk.Topic.QuickStart.csproj @@ -5,13 +5,14 @@ net8.0 enable enable + Ydb.Sdk.Topic.QuickStart - - + + - + diff --git a/examples/src/YdbExamples.sln b/examples/YdbExamples.sln similarity index 50% rename from examples/src/YdbExamples.sln rename to examples/YdbExamples.sln index 3f99e3df..8966b920 100644 --- a/examples/src/YdbExamples.sln +++ b/examples/YdbExamples.sln @@ -3,38 +3,36 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 16 VisualStudioVersion = 16.0.31205.134 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Common", "Common\Common.csproj", "{59F508A9-5EE0-4A6C-9580-89FC8C6CD4CE}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ydb.Sdk.QueryService.QuickStart", "Ydb.Sdk.QueryService.QuickStart\Ydb.Sdk.QueryService.QuickStart.csproj", "{0BA2CD4F-BF38-4C0D-878B-3D0C2C2970D9}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BasicExample", "BasicExample\BasicExample.csproj", "{9DAD5FF3-B7C2-4A9E-B4B2-A0FBD6097727}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ydb.Sdk.AdoNet.QuickStart", "Ydb.Sdk.AdoNet.QuickStart\Ydb.Sdk.AdoNet.QuickStart.csproj", "{5030F1BC-E974-46BB-9381-C4356E120D96}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "QueryExample", "QueryExample\QueryExample.csproj", "{0BA2CD4F-BF38-4C0D-878B-3D0C2C2970D9}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ydb.Sdk.AdoNet.Dapper.QuickStart", "Ydb.Sdk.AdoNet.Dapper.QuickStart\Ydb.Sdk.AdoNet.Dapper.QuickStart.csproj", "{AC8F1B10-31EB-4A29-BF35-7F49B06E1FA8}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AdoNet", "AdoNet\AdoNet.csproj", "{5030F1BC-E974-46BB-9381-C4356E120D96}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ydb.Sdk.AdoNet.Yandex.Cloud", "Ydb.Sdk.AdoNet.Yandex.Cloud\Ydb.Sdk.AdoNet.Yandex.Cloud.csproj", "{753E4F33-CB08-47B9-864F-4CC037B278C4}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DapperExample", "DapperExample\DapperExample.csproj", "{AC8F1B10-31EB-4A29-BF35-7F49B06E1FA8}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "YC", "YC\YC.csproj", "{753E4F33-CB08-47B9-864F-4CC037B278C4}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Topic", "Topic\Topic.csproj", "{0FB9A1C2-4B0C-4AE4-9FA2-E0ED37802A6E}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ydb.Sdk.Topic.QuickStart", "Ydb.Sdk.Topic.QuickStart\Ydb.Sdk.Topic.QuickStart.csproj", "{0FB9A1C2-4B0C-4AE4-9FA2-E0ED37802A6E}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EntityFrameworkCore.Ydb.QuickStart", "EntityFrameworkCore.Ydb.QuickStart\EntityFrameworkCore.Ydb.QuickStart.csproj", "{0CE9DF93-1411-4E73-BA88-A66018FAB948}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EntityFrameworkCore.Ydb.Yandex.Cloud", "EntityFrameworkCore.Ydb.Yandex.Cloud\EntityFrameworkCore.Ydb.Yandex.Cloud.csproj", "{8F7266C7-75BB-4753-9FB2-BDF4678AF73B}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AddEntity.Sample", "EntityFrameworkCore.Ydb.Samples\AddEntity.Sample\AddEntity.Sample.csproj", "{CFEC4D14-902F-4E27-9F6C-8E150AE00009}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Schema.OneToMany", "EntityFrameworkCore.Ydb.Samples\Schema.OneToMany\Schema.OneToMany.csproj", "{5D8A1318-FD60-4BDD-9DB3-BF050CCC9755}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Schema.OneToOne", "EntityFrameworkCore.Ydb.Samples\Schema.OneToOne\Schema.OneToOne.csproj", "{DBCEC1B1-FD84-4F9B-84B9-7BDA749C6DF2}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Schema.ManyToMany", "EntityFrameworkCore.Ydb.Samples\Schema.ManyToMany\Schema.ManyToMany.csproj", "{08439995-062A-497A-9130-0F6429B0219E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Database.Operations.Tutorial", "EntityFrameworkCore.Ydb.Samples\Database.Operations.Tutorial\Database.Operations.Tutorial.csproj", "{B2B0786F-7545-4717-8D04-26DF9C181607}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {59F508A9-5EE0-4A6C-9580-89FC8C6CD4CE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {59F508A9-5EE0-4A6C-9580-89FC8C6CD4CE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {59F508A9-5EE0-4A6C-9580-89FC8C6CD4CE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {59F508A9-5EE0-4A6C-9580-89FC8C6CD4CE}.Release|Any CPU.Build.0 = Release|Any CPU - {9DAD5FF3-B7C2-4A9E-B4B2-A0FBD6097727}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {9DAD5FF3-B7C2-4A9E-B4B2-A0FBD6097727}.Debug|Any CPU.Build.0 = Debug|Any CPU - {9DAD5FF3-B7C2-4A9E-B4B2-A0FBD6097727}.Release|Any CPU.ActiveCfg = Release|Any CPU - {9DAD5FF3-B7C2-4A9E-B4B2-A0FBD6097727}.Release|Any CPU.Build.0 = Release|Any CPU {0BA2CD4F-BF38-4C0D-878B-3D0C2C2970D9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {0BA2CD4F-BF38-4C0D-878B-3D0C2C2970D9}.Debug|Any CPU.Build.0 = Debug|Any CPU {0BA2CD4F-BF38-4C0D-878B-3D0C2C2970D9}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -63,6 +61,26 @@ Global {8F7266C7-75BB-4753-9FB2-BDF4678AF73B}.Debug|Any CPU.Build.0 = Debug|Any CPU {8F7266C7-75BB-4753-9FB2-BDF4678AF73B}.Release|Any CPU.ActiveCfg = Release|Any CPU {8F7266C7-75BB-4753-9FB2-BDF4678AF73B}.Release|Any CPU.Build.0 = Release|Any CPU + {CFEC4D14-902F-4E27-9F6C-8E150AE00009}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CFEC4D14-902F-4E27-9F6C-8E150AE00009}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CFEC4D14-902F-4E27-9F6C-8E150AE00009}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CFEC4D14-902F-4E27-9F6C-8E150AE00009}.Release|Any CPU.Build.0 = Release|Any CPU + {5D8A1318-FD60-4BDD-9DB3-BF050CCC9755}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5D8A1318-FD60-4BDD-9DB3-BF050CCC9755}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5D8A1318-FD60-4BDD-9DB3-BF050CCC9755}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5D8A1318-FD60-4BDD-9DB3-BF050CCC9755}.Release|Any CPU.Build.0 = Release|Any CPU + {DBCEC1B1-FD84-4F9B-84B9-7BDA749C6DF2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DBCEC1B1-FD84-4F9B-84B9-7BDA749C6DF2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DBCEC1B1-FD84-4F9B-84B9-7BDA749C6DF2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DBCEC1B1-FD84-4F9B-84B9-7BDA749C6DF2}.Release|Any CPU.Build.0 = Release|Any CPU + {08439995-062A-497A-9130-0F6429B0219E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {08439995-062A-497A-9130-0F6429B0219E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {08439995-062A-497A-9130-0F6429B0219E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {08439995-062A-497A-9130-0F6429B0219E}.Release|Any CPU.Build.0 = Release|Any CPU + {B2B0786F-7545-4717-8D04-26DF9C181607}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B2B0786F-7545-4717-8D04-26DF9C181607}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B2B0786F-7545-4717-8D04-26DF9C181607}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B2B0786F-7545-4717-8D04-26DF9C181607}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/examples/src/YdbExamples.sln.DotSettings b/examples/YdbExamples.sln.DotSettings similarity index 100% rename from examples/src/YdbExamples.sln.DotSettings rename to examples/YdbExamples.sln.DotSettings diff --git a/examples/src/BasicExample/BasicExample.cs b/examples/src/BasicExample/BasicExample.cs deleted file mode 100644 index 494a2369..00000000 --- a/examples/src/BasicExample/BasicExample.cs +++ /dev/null @@ -1,79 +0,0 @@ -using System.Security.Cryptography.X509Certificates; -using Microsoft.Extensions.Logging; -using Ydb.Sdk.Auth; -using Ydb.Sdk.Services.Table; - -namespace Ydb.Sdk.Examples; - -internal partial class BasicExample : TableExampleBase -{ - private BasicExample(TableClient client, string database, string path) - : base(client, database, path) - { - } - - public static async Task Run( - string endpoint, - string database, - ICredentialsProvider? credentialsProvider, - X509Certificate? customServerCertificate, - string path, - ILoggerFactory loggerFactory) - { - var config = new DriverConfig( - endpoint: endpoint, - database: database, - credentials: credentialsProvider, - customServerCertificate: customServerCertificate - ); - - await using var driver = await Driver.CreateInitialized( - config: config, - loggerFactory: loggerFactory - ); - - using var tableClient = new TableClient(driver, new TableClientConfig()); - - var example = new BasicExample(tableClient, database, path); - - await example.SchemeQuery(); - await example.FillData(); - await example.SimpleSelect(1); - await example.SimpleUpsert(10, "Coming soon", DateTime.UtcNow); - await example.SimpleSelect(10); - await example.InteractiveTx(); - await example.ReadTable(); - await example.ScanQuery(DateTime.Parse("2007-01-01")); - } - - private static ExecuteDataQuerySettings DefaultDataQuerySettings => - new() - { - // Indicates that client is no longer interested in the result of operation after the - // specified duration starting from the moment when operation arrives at the server. - // Status code TIMEOUT will be returned from server in case when operation result in - // not available in the specified time period. This status code doesn't indicate the result - // of operation, it might be completed or cancelled. - OperationTimeout = TimeSpan.FromSeconds(1), - - // Transport timeout from the moment operation was sent to server. It is useful in case - // of possible network issues, to that query doesn't hang forever. - // It is recommended to set this value to a larger value than OperationTimeout to give - // server some time to issue a response. - TransportTimeout = TimeSpan.FromSeconds(5), - - // Keep query compilation result in query cache or not. Should be false for ad-hoc queries, - // and true (default) for high-RPS queries. - KeepInQueryCache = false - }; - - private ExecuteDataQuerySettings DefaultCachedDataQuerySettings - { - get - { - var settings = DefaultDataQuerySettings; - settings.KeepInQueryCache = true; - return settings; - } - } -} \ No newline at end of file diff --git a/examples/src/BasicExample/BasicExample.csproj b/examples/src/BasicExample/BasicExample.csproj deleted file mode 100644 index 912bc598..00000000 --- a/examples/src/BasicExample/BasicExample.csproj +++ /dev/null @@ -1,29 +0,0 @@ - - - - - Exe - net6.0 - enable - enable - Ydb.Sdk.Examples.BasicExample - Ydb.Sdk.Examples - - - - git - https://github.com/ydb-platform/ydb-dotnet-sdk - https://github.com/ydb-platform/ydb-dotnet-sdk - YANDEX LLC - - - - - - - - - - - - diff --git a/examples/src/BasicExample/DataQuery.cs b/examples/src/BasicExample/DataQuery.cs deleted file mode 100644 index 4f5d1248..00000000 --- a/examples/src/BasicExample/DataQuery.cs +++ /dev/null @@ -1,85 +0,0 @@ -using Ydb.Sdk.Services.Table; -using Ydb.Sdk.Value; - -namespace Ydb.Sdk.Examples; - -internal partial class BasicExample -{ - private async Task SimpleSelect(ulong id) - { - var response = await Client.SessionExec(async session => - { - var query = @$" - PRAGMA TablePathPrefix('{BasePath}'); - - DECLARE $id AS Uint64; - - SELECT - series_id, - title, - release_date - FROM series - WHERE series_id = $id; - "; - - return await session.ExecuteDataQuery( - query: query, - txControl: TxControl.BeginSerializableRW().Commit(), - parameters: new Dictionary - { - { "$id", YdbValue.MakeUint64(id) } - }, - settings: DefaultCachedDataQuerySettings - ); - }); - - response.Status.EnsureSuccess(); - - var queryResponse = (ExecuteDataQueryResponse)response; - var resultSet = queryResponse.Result.ResultSets[0]; - - Console.WriteLine($"> SimpleSelect, " + - $"columns: {resultSet.Columns.Count}, " + - $"rows: {resultSet.Rows.Count}, " + - $"truncated: {resultSet.Truncated}"); - - foreach (var row in resultSet.Rows) - { - Console.WriteLine($"> Series, " + - $"series_id: {(ulong?)row["series_id"]}, " + - $"title: {(string?)row["title"]}, " + - $"release_date: {(DateTime?)row["release_date"]}"); - } - } - - private async Task SimpleUpsert(ulong id, string title, DateTime date) - { - var response = await Client.SessionExec(async session => - { - var query = @$" - PRAGMA TablePathPrefix('{BasePath}'); - - DECLARE $id AS Uint64; - DECLARE $title AS Utf8; - DECLARE $release_date AS Date; - - UPSERT INTO series (series_id, title, release_date) VALUES - ($id, $title, $release_date); - "; - - return await session.ExecuteDataQuery( - query: query, - txControl: TxControl.BeginSerializableRW().Commit(), - parameters: new Dictionary - { - { "$id", YdbValue.MakeUint64(id) }, - { "$title", YdbValue.MakeUtf8(title) }, - { "$release_date", YdbValue.MakeDate(date) } - }, - settings: DefaultCachedDataQuerySettings - ); - }); - - response.Status.EnsureSuccess(); - } -} \ No newline at end of file diff --git a/examples/src/BasicExample/FillData.cs b/examples/src/BasicExample/FillData.cs deleted file mode 100644 index 48e6d4c8..00000000 --- a/examples/src/BasicExample/FillData.cs +++ /dev/null @@ -1,55 +0,0 @@ -using Ydb.Sdk.Services.Table; - -namespace Ydb.Sdk.Examples; - -internal partial class BasicExample -{ - // Fill sample tables with initial data. - private async Task FillData() - { - var response = await Client.SessionExec(async session => - { - var query = @$" - PRAGMA TablePathPrefix('{BasePath}'); - - DECLARE $seriesData AS List>; - - DECLARE $seasonsData AS List>; - - DECLARE $episodesData AS List>; - - REPLACE INTO series - SELECT * FROM AS_TABLE($seriesData); - - REPLACE INTO seasons - SELECT * FROM AS_TABLE($seasonsData); - - REPLACE INTO episodes - SELECT * FROM AS_TABLE($episodesData); - "; - - return await session.ExecuteDataQuery( - query: query, - txControl: TxControl.BeginSerializableRW().Commit(), - parameters: DataUtils.GetDataParams(), - settings: DefaultDataQuerySettings - ); - }); - - response.Status.EnsureSuccess(); - } -} \ No newline at end of file diff --git a/examples/src/BasicExample/InteractiveTx.cs b/examples/src/BasicExample/InteractiveTx.cs deleted file mode 100644 index e49390bc..00000000 --- a/examples/src/BasicExample/InteractiveTx.cs +++ /dev/null @@ -1,72 +0,0 @@ -using Ydb.Sdk.Services.Table; -using Ydb.Sdk.Value; - -namespace Ydb.Sdk.Examples; - -internal partial class BasicExample -{ - private async Task InteractiveTx() - { - var execResponse = await Client.SessionExec(async session => - { - var query1 = @$" - PRAGMA TablePathPrefix('{BasePath}'); - - DECLARE $series_id AS Uint64; - DECLARE $season_id AS Uint64; - - SELECT first_aired FROM seasons - WHERE series_id = $series_id AND season_id = $season_id; - "; - - // Execute first query (no transaction commit) - var response = await session.ExecuteDataQuery( - query: query1, - txControl: TxControl.BeginSerializableRW(), - parameters: new Dictionary - { - { "$series_id", YdbValue.MakeUint64(1) }, - { "$season_id", YdbValue.MakeUint64(3) } - }, - settings: DefaultCachedDataQuerySettings - ); - - if (!response.Status.IsSuccess || response.Tx is null) - { - return response; - } - - // Perform some client logic - var firstAired = (DateTime?)response.Result.ResultSets[0].Rows[0]["first_aired"]; - var newAired = firstAired!.Value.AddDays(2); - - var query2 = @$" - PRAGMA TablePathPrefix('{BasePath}'); - - DECLARE $series_id AS Uint64; - DECLARE $season_id AS Uint64; - DECLARE $air_date AS Date; - - UPSERT INTO seasons (series_id, season_id, first_aired) VALUES - ($series_id, $season_id, $air_date); - "; - - // Execute second query and commit transaction. - response = await session.ExecuteDataQuery( - query: query2, - TxControl.Tx(response.Tx).Commit(), - parameters: new Dictionary - { - { "$series_id", YdbValue.MakeUint64(1) }, - { "$season_id", YdbValue.MakeUint64(3) }, - { "$air_date", YdbValue.MakeDate(newAired) } - }, - settings: DefaultCachedDataQuerySettings - ); - - return response; - }); - - execResponse.Status.EnsureSuccess(); - } -} \ No newline at end of file diff --git a/examples/src/BasicExample/Program.cs b/examples/src/BasicExample/Program.cs deleted file mode 100644 index 2c5895a1..00000000 --- a/examples/src/BasicExample/Program.cs +++ /dev/null @@ -1,54 +0,0 @@ -using CommandLine; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Logging.Abstractions; - -namespace Ydb.Sdk.Examples; - -internal class CmdOptions -{ - [Option('e', "endpoint", Required = true, HelpText = "Database endpoint")] - public string Endpoint { get; set; } = ""; - - [Option('d', "database", Required = true, HelpText = "Database name")] - public string Database { get; set; } = ""; - - [Option('p', "path", HelpText = "Base path for tables")] - public string Path { get; set; } = "ydb-dotnet-basic"; - - [Option("anonymous", Required = false, HelpText = "Fallback anonymous")] - public bool FallbackAnonymous { get; set; } = false; -} - -internal static class Program -{ - private static ServiceProvider GetServiceProvider() => new ServiceCollection() - .AddLogging(configure => configure.AddConsole().SetMinimumLevel(LogLevel.Information)) - .BuildServiceProvider(); - - private static async Task Run(CmdOptions cmdOptions) - { - await using var serviceProvider = GetServiceProvider(); - var loggerFactory = serviceProvider.GetService(); - - loggerFactory ??= NullLoggerFactory.Instance; - - await BasicExample.Run( - endpoint: cmdOptions.Endpoint, - database: cmdOptions.Database, - credentialsProvider: await AuthUtils.MakeCredentialsFromEnv( - fallbackAnonymous: cmdOptions.FallbackAnonymous, - loggerFactory: loggerFactory), - customServerCertificate: AuthUtils.GetCustomServerCertificate(), - path: cmdOptions.Path, - loggerFactory: loggerFactory - ); - } - - private static async Task Main(string[] args) - { - AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true); - - await Parser.Default.ParseArguments(args).WithParsedAsync(Run); - } -} \ No newline at end of file diff --git a/examples/src/BasicExample/ReadTable.cs b/examples/src/BasicExample/ReadTable.cs deleted file mode 100644 index 7433db5b..00000000 --- a/examples/src/BasicExample/ReadTable.cs +++ /dev/null @@ -1,32 +0,0 @@ -using Ydb.Sdk.Services.Table; - -namespace Ydb.Sdk.Examples; - -internal partial class BasicExample -{ - private async Task ReadTable() - { - var readStream = await Client.ReadTable( - FullTablePath("seasons"), - new ReadTableSettings - { - Columns = new List { "series_id", "season_id", "first_aired" }, - RowLimit = 5, - Ordered = true - }); - - while (await readStream.Next()) - { - readStream.Response.EnsureSuccess(); - var resultSet = readStream.Response.Result.ResultSet; - - foreach (var row in resultSet.Rows) - { - Console.WriteLine($"> ReadTable seasons, " + - $"series_id: {(ulong?)row["series_id"]}, " + - $"season_id: {(ulong?)row["season_id"]}, " + - $"first_aired: {(DateTime?)row["first_aired"]}"); - } - } - } -} \ No newline at end of file diff --git a/examples/src/BasicExample/ScanQuery.cs b/examples/src/BasicExample/ScanQuery.cs deleted file mode 100644 index 4260bf6f..00000000 --- a/examples/src/BasicExample/ScanQuery.cs +++ /dev/null @@ -1,45 +0,0 @@ -using Ydb.Sdk.Value; - -namespace Ydb.Sdk.Examples; - -internal partial class BasicExample -{ - private async Task ScanQuery(DateTime airFrom) - { - var query = @$" - PRAGMA TablePathPrefix('{BasePath}'); - - DECLARE $air_from AS Date; - - SELECT series_id, season_id, COUNT(*) AS episodes_count - FROM episodes - WHERE air_date >= $air_from - GROUP BY series_id, season_id - ORDER BY series_id, season_id; - "; - - var scanStream = await Client.ExecuteScanQuery( - query, - new Dictionary - { - { "$air_from", YdbValue.MakeDate(airFrom) } - }); - - while (await scanStream.Next()) - { - scanStream.Response.EnsureSuccess(); - - var resultSet = scanStream.Response.Result.ResultSetPart; - if (resultSet != null) - { - foreach (var row in resultSet.Rows) - { - Console.WriteLine($"> ScanQuery, " + - $"series_id: {(ulong?)row["series_id"]}, " + - $"season_id: {(ulong?)row["season_id"]}, " + - $"episodes_count: {(ulong)row["episodes_count"]}"); - } - } - } - } -} \ No newline at end of file diff --git a/examples/src/BasicExample/SchemeQuery.cs b/examples/src/BasicExample/SchemeQuery.cs deleted file mode 100644 index 24bbaa23..00000000 --- a/examples/src/BasicExample/SchemeQuery.cs +++ /dev/null @@ -1,41 +0,0 @@ -namespace Ydb.Sdk.Examples; - -internal partial class BasicExample -{ - // Execute Scheme (DDL) query to create sample tables. - private async Task SchemeQuery() - { - var response = await Client.SessionExec(async session => - await session.ExecuteSchemeQuery(@$" - PRAGMA TablePathPrefix('{BasePath}'); - - CREATE TABLE series ( - series_id Uint64, - title Utf8, - series_info Utf8, - release_date Date, - PRIMARY KEY (series_id) - ); - - CREATE TABLE seasons ( - series_id Uint64, - season_id Uint64, - title Utf8, - first_aired Date, - last_aired Date, - PRIMARY KEY (series_id, season_id) - ); - - CREATE TABLE episodes ( - series_id Uint64, - season_id Uint64, - episode_id Uint64, - title Utf8, - air_date Date, - PRIMARY KEY (series_id, season_id, episode_id) - ); - ")); - - response.Status.EnsureSuccess(); - } -} \ No newline at end of file diff --git a/examples/src/Common/AuthUtils.cs b/examples/src/Common/AuthUtils.cs deleted file mode 100644 index fd976e2d..00000000 --- a/examples/src/Common/AuthUtils.cs +++ /dev/null @@ -1,61 +0,0 @@ -using System; -using System.Security.Cryptography.X509Certificates; -using System.Threading.Tasks; -using Microsoft.Extensions.Logging; -using Ydb.Sdk.Auth; -using Ydb.Sdk.Yc; - -namespace Ydb.Sdk.Examples; - -public static class AuthUtils -{ - public static async Task MakeCredentialsFromEnv( - bool fallbackAnonymous = false, - ILoggerFactory? loggerFactory = null) - { - var saFileValue = Environment.GetEnvironmentVariable("YDB_SERVICE_ACCOUNT_KEY_FILE_CREDENTIALS"); - if (!string.IsNullOrEmpty(saFileValue)) - { - var saProvider = new ServiceAccountProvider( - saFilePath: saFileValue, - loggerFactory: loggerFactory); - await saProvider.Initialize(); - return saProvider; - } - - var anonymousValue = Environment.GetEnvironmentVariable("YDB_ANONYMOUS_CREDENTIALS"); - if (anonymousValue != null && IsTrueValue(anonymousValue)) - { - return null; - } - - var metadataValue = Environment.GetEnvironmentVariable("YDB_METADATA_CREDENTIALS"); - if (metadataValue != null && IsTrueValue(metadataValue)) - { - var metadataProvider = new MetadataProvider( - loggerFactory: loggerFactory); - await metadataProvider.Initialize(); - return metadataProvider; - } - - var tokenValue = Environment.GetEnvironmentVariable("YDB_ACCESS_TOKEN_CREDENTIALS"); - if (!string.IsNullOrEmpty(tokenValue)) - { - return new TokenProvider(tokenValue); - } - - if (fallbackAnonymous) - { - return null; - } - - throw new InvalidOperationException("Failed to parse credentials from environmet, no valid options found."); - } - - public static X509Certificate GetCustomServerCertificate() => YcCerts.GetDefaultServerCertificate(); - - private static bool IsTrueValue(string value) => - value == "1" || - value.ToLower() == "yes" || - value.ToLower() == "true"; -} \ No newline at end of file diff --git a/examples/src/Common/Common.csproj b/examples/src/Common/Common.csproj deleted file mode 100644 index a7cdb16c..00000000 --- a/examples/src/Common/Common.csproj +++ /dev/null @@ -1,26 +0,0 @@ - - - - net6.0 - Ydb.Sdk.Examples.Common - Ydb.Sdk.Examples - enable - - - - git - https://github.com/ydb-platform/ydb-dotnet-sdk - https://github.com/ydb-platform/ydb-dotnet-sdk - YANDEX LLC - - - - - - - - - - - - diff --git a/examples/src/Common/TableExampleBase.cs b/examples/src/Common/TableExampleBase.cs deleted file mode 100644 index 168f8f51..00000000 --- a/examples/src/Common/TableExampleBase.cs +++ /dev/null @@ -1,17 +0,0 @@ -using Ydb.Sdk.Services.Table; - -namespace Ydb.Sdk.Examples; - -public class TableExampleBase -{ - protected TableClient Client { get; } - protected string BasePath { get; } - - protected TableExampleBase(TableClient client, string database, string path) - { - Client = client; - BasePath = string.Join('/', database, path); - } - - protected string FullTablePath(string table) => string.Join('/', BasePath, table); -} \ No newline at end of file diff --git a/examples/src/YC/CmdOptions.cs b/examples/src/YC/CmdOptions.cs deleted file mode 100644 index 0c6f3b11..00000000 --- a/examples/src/YC/CmdOptions.cs +++ /dev/null @@ -1,15 +0,0 @@ -using CommandLine; - -namespace YcCloud; - -internal class CmdOptions -{ - [Option('h', "host", Required = true, HelpText = "Database host")] - public string Host { get; set; } = null!; - - [Option('d', "database", Required = true, HelpText = "Database name")] - public string Database { get; set; } = null!; - - [Option("saFilePath", Required = true, HelpText = "Sa Key")] - public string SaFilePath { get; set; } = null!; -} \ No newline at end of file diff --git a/src/Ydb.Sdk/README.md b/src/Ydb.Sdk/README.md index b979a78e..6c40bfd7 100644 --- a/src/Ydb.Sdk/README.md +++ b/src/Ydb.Sdk/README.md @@ -59,7 +59,7 @@ while (await ydbDataReader.ReadAsync()) ## More examples -- [AdoNet simple guide](./../../examples/src/AdoNet) -- [AdoNet connect to Yandex Cloud](./../../examples/src/YC) -- [Dapper example](./../../examples/src/DapperExample) -- [Topic client](./../../examples/src/Topic) +- [AdoNet simple guide](./../../examples/Ydb.Sdk.AdoNet.QuickStart) +- [AdoNet connect to Yandex Cloud](./../../examples/Ydb.Sdk.AdoNet.Yandex.Cloud) +- [Dapper example](./../../examples/Ydb.Sdk.AdoNet.Dapper.QuickStart) +- [Topic client](./../../examples/Ydb.Sdk.Topic.QuickStart)