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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,11 @@ jobs:
run: |
cd ./examples/src/Topic
dotnet run
- name: Run EF example
- name: Run Entity Framework Core example
run: |
cd ./examples/src/EF
cd ./examples/src/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
14 changes: 0 additions & 14 deletions examples/src/EF/EF.csproj

This file was deleted.

57 changes: 0 additions & 57 deletions examples/src/EF/Program.cs

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk">

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

<ItemGroup>
<ProjectReference Include="..\..\..\src\EFCore.Ydb\src\EntityFrameworkCore.Ydb.csproj" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.5">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>
</Project>
88 changes: 88 additions & 0 deletions examples/src/EntityFrameworkCore.Ydb.QuickStart/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
using EntityFrameworkCore.Ydb.Extensions;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Design;

await using var db = new BloggingContext();

// Create
Console.WriteLine("Inserting a new blog");
db.Add(new Blog { Url = "http://blogs.msdn.com/adonet" });
await db.SaveChangesAsync();

// Read
Console.WriteLine("Querying for a blog");
var blog = await db.Blogs
.OrderBy(b => b.BlogId)
.FirstAsync();

// Update
Console.WriteLine("Updating the blog and adding a post");
blog.Url = "https://devblogs.microsoft.com/dotnet";
blog.Posts.Add(
new Post { Title = "Hello World", Content = "I wrote an app using EF Core!" });
await db.SaveChangesAsync();

// Delete
Console.WriteLine("Delete the blog");
db.Remove(blog);
await db.SaveChangesAsync();

internal class BloggingContextFactory : IDesignTimeDbContextFactory<BloggingContext>
{
public BloggingContext CreateDbContext(string[] args)
{
var optionsBuilder = new DbContextOptionsBuilder<BloggingContext>();

// 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 BloggingContext(
optionsBuilder.UseYdb("Host=localhost;Port=2136;Database=/local",
builder => builder.DisableRetryOnFailure()
).Options
);
}
}

internal class BloggingContext : DbContext
{
internal BloggingContext()
{
}

internal BloggingContext(DbContextOptions<BloggingContext> options) : base(options)
{
}

public DbSet<Blog> Blogs { get; set; }
public DbSet<Post> Posts { get; set; }

protected override void OnConfiguring(DbContextOptionsBuilder options) =>
options.UseYdb("Host=localhost;Port=2136;Database=/local");
}

internal class Blog
{
public int BlogId { get; init; }

public string Url { get; set; } = string.Empty;

// ReSharper disable once CollectionNeverQueried.Global
public List<Post> Posts { get; init; } = [];
}

internal class Post
{
public int PostId { get; init; }

public string Title { get; init; } = string.Empty;

public string Content { get; init; } = string.Empty;

public Blog Blog { get; init; } = null!;
}
16 changes: 16 additions & 0 deletions examples/src/EntityFrameworkCore.Ydb.QuickStart/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# 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)
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
dotnet add package Microsoft.EntityFrameworkCore.Design
dotnet ef migrations add InitialCreate
dotnet ef database update
```
3. Run the app: `dotnet run`
2 changes: 1 addition & 1 deletion examples/src/YdbExamples.sln
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "YC", "YC\YC.csproj", "{753E
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Topic", "Topic\Topic.csproj", "{0FB9A1C2-4B0C-4AE4-9FA2-E0ED37802A6E}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EF", "EF\EF.csproj", "{0CE9DF93-1411-4E73-BA88-A66018FAB948}"
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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ public class YdbOptionsExtension : RelationalOptionsExtension

public X509Certificate2Collection? ServerCertificates { get; private set; }

public bool DisableRetryExecutionStrategy { get; private set; }

private DbContextOptionsExtensionInfo? _info;

public YdbOptionsExtension()
Expand All @@ -23,11 +25,13 @@ private YdbOptionsExtension(YdbOptionsExtension copyFrom) : base(copyFrom)
{
CredentialsProvider = copyFrom.CredentialsProvider;
ServerCertificates = copyFrom.ServerCertificates;
DisableRetryExecutionStrategy = copyFrom.DisableRetryExecutionStrategy;
}

protected override RelationalOptionsExtension Clone() => new YdbOptionsExtension(this);

public override void ApplyServices(IServiceCollection services) => services.AddEntityFrameworkYdb();
public override void ApplyServices(IServiceCollection services) =>
services.AddEntityFrameworkYdb(!DisableRetryExecutionStrategy);

public override DbContextOptionsExtensionInfo Info => _info ??= new ExtensionInfo(this);

Expand All @@ -49,6 +53,15 @@ public YdbOptionsExtension WithServerCertificates(X509Certificate2Collection? se
return clone;
}

public YdbOptionsExtension DisableRetryOnFailure()
{
var clone = (YdbOptionsExtension)Clone();

clone.DisableRetryExecutionStrategy = true;

return clone;
}

private sealed class ExtensionInfo(YdbOptionsExtension extension) : RelationalExtensionInfo(extension)
{
public override bool IsDatabaseProvider => true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,7 @@ public YdbDbContextOptionsBuilder WithCredentialsProvider(ICredentialsProvider?

public YdbDbContextOptionsBuilder WithServerCertificates(X509Certificate2Collection? serverCertificates) =>
WithOption(optionsBuilder => optionsBuilder.WithServerCertificates(serverCertificates));

public YdbDbContextOptionsBuilder DisableRetryOnFailure() =>
WithOption(optionsBuilder => optionsBuilder.DisableRetryOnFailure());
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ public class YdbRelationalConnection : RelationalConnection, IYdbRelationalConne
{
private readonly ICredentialsProvider? _credentialsProvider;
private readonly X509Certificate2Collection? _serverCertificates;
private readonly bool _disableRetryExecuteStrategy;

public YdbRelationalConnection(RelationalConnectionDependencies dependencies) : base(dependencies)
{
Expand All @@ -21,6 +22,7 @@ public YdbRelationalConnection(RelationalConnectionDependencies dependencies) :

_credentialsProvider = ydbOptionsExtension.CredentialsProvider;
_serverCertificates = ydbOptionsExtension.ServerCertificates;
_disableRetryExecuteStrategy = ydbOptionsExtension.DisableRetryExecutionStrategy;
}

protected override DbConnection CreateDbConnection()
Expand All @@ -40,6 +42,10 @@ public IYdbRelationalConnection Clone()
{
builder.WithCredentialsProvider(_credentialsProvider);
builder.WithServerCertificates(_serverCertificates);
if (_disableRetryExecuteStrategy)
{
builder.DisableRetryOnFailure();
}
}).Options;

return new YdbRelationalConnection(Dependencies with { ContextOptions = options });
Expand Down
Loading